Skip to content
This repository has been archived by the owner on Sep 19, 2020. It is now read-only.

Core Topic – Creating a Custom View

Oliver Cooper edited this page Jan 11, 2016 · 1 revision

Views are one of the most important and frequently used class in Silica. Anything from a Button to a Window is a View.

Contents


Default Views

These are all the View subclasses that come with Silica. The tree represents their class hierarchy (i.e., what class they extend from).

Role of a View

What a View Does

What a View Doesn't

Creating a Custom View

To achieve the interface you want, it may be necessary to create your own view. If it looks like one of the default views won't do what you want, chances are you need to create your own.

Note

Creating custom views is an advanced topic. If you're not comfortable with the basics of Silica you might want to learn a bit more first. Knowing how Silica works is vital to creating custom views.

Prerequisites

Before you begin you should have a good understanding of the following topics:

Creating a View Subclass

To get started you will need to create a View subclass. As the Style Guide notes, if it assists with conveying what the your View subclass is, append 'View' to the end of your class' name. This isn't overly common for View subclasses normally (except for `Container subclasses)

class "CustomView" extends "View" {

}

At this point you should be able to add CustomView to an interface file without errors appearing. It will be invisible at the moment, that is normal. Read the Changing a View's Appearance section for how to change this.

Changing a View's Appearance

A View subclass' appearance determines what it looks like.

How View Drawing Works

View subclasses draw to the screen by adding GraphicsObject instances to the View's Canvas. A Canvas is simply a GraphicsObject that can hold child GraphicsObject instances within itself.

The main point to know is that each View has a canvas, accessed by self.canvas, and if you want to draw something you add a GraphicsObject subclass to that canvas.

Drawing is a very detailed topic, if you want to read more about it, look at the Drawing page.

Before You Begin

Create a Theme Node

As you are defining what a View looks like you will need to set properties in a theme. If you aren't already using a theme file in your application, create a blank one now, make your interface use it and add a node for your View subclass.

<Theme extends="default">

    <CustomView>
    </CustomView>
    
</Theme>

Create the :initialiseCanvas Function

The initial setup of your Canvas' GraphicsObject instances must be done within :initialiseCanvas. You must also call :initialiseCanvas on super, if you do not your view will not have a Canvas, meaning no drawing!

function CustomView:initialiseCanvas()
    self.super:initialiseCanvas()
    -- your code here
end

Setting a View's Fill Colour

Adding a Theme Property

A blank View subclass will be invisible at this stage. To give it a fill (background) colour we change the fillColour property of the View's Canvas.

Create a fillColour property in your View's theme node.

<CustomView>
    <fillColour type="Graphics.colours" default="RED"/>
</CustomView>

Linking the Theme Property to the Canvas

Once you have defined a property in your theme file you then need to link it.

Under the super call, make the connection.

function CustomView:initialiseCanvas()
    self.super:initialiseCanvas()
    --                             canvas property | theme property
    --                                      \/            \/
    self.theme:connect( self.canvas, "fillColour", "fillColour" )
end

What this is doing is linking the fillColour property you defined in your theme (the last argument) to the fillColour property of self.canvas. If the theme changes or style changes this will automatically be updated.

Because the theme property and Canvas property have the same name, you can omit the last argument, it will assume the same name. Hence, the below will result in the same as the code above.

self.theme:connect( self.canvas, "fillColour" )

You should now see your view as a plain red colour rectangle. If you can't see it make sure you've set the width and height large enough to see it.

Red rectangle

Adding Basics Shapes to a View

Chances are most of your View subclasses won't be plain coloured rectangles. Let's make it a more interesting shape, a rounded rectangle.

Note

Silica comes with a wide variety of different shaped GraphicsObject subclasses you can use, as well as the Path object, allowing for linear, bezier and circular curves. You can read about the different subclasses of GraphicsObject on the Drawing page as well as how to make your own. This section will focus on how to use GraphicsObject subclasses with custom views, rather than on the GraphicsObject subclasses.

Adding a Theme Property

Rounded rectangles allow you to choose their corner radius, to do this you will need to create another theme property.

Add a cornerRadius property to you theme node. Unlike previously, this time we need to use the number type, rather than the Graphics.colours type as the radius is a number.

<cornerRadius type="number" default="6"/>

Inserting the Rounded Rectangle

If you still have the code connecting self.canvas to fillColour, remove that now.

First we need to create our rounded rectangle and insert it in to our canvas. Insert this code after the super call.

local roundedRectangleObject = self.canvas:insert( RoundedRectangle( x, y, width, height ) )

Replace the x, y, width and height properties. With the values you want. To make it cover your entire view, you would use the following. All coordinates are relative to the View, so a position 1, 1 will be in the top left corner. (Remember: self is referring to the View)

RoundedRectangle( 1, 1, self.width, self.height )

Calling RoundedRectangle() creates an instance of a RoundedRectangle, a subclass of GraphicsObject. self.canvas:insert then inserts that RoundedRectangle in to the canvas. The :insert function returns the RoundedRectangle instance.

Linking the Theme Properties to the Rounded Rectangle

Once you have inserted the GraphicsObject you need to link it to its theme properties. The properties of RoundedRectangle we want to link are fillColour and radius.

Let's do that now. Add this after inserting the RoundedRectangle.

local theme = self.theme
theme:connect( roundedRectangleObject, "fillColour" )
theme:connect( roundedRectangleObject, "radius", "cornerRadius" )
Note

You'll notice that we are using cornerRadius for the theme property, but radius for the RoundedRectangle property. Using cornerRadius is just personal preference to distinguish which radius it is when there are multiple rounded rectangles.

Testing Time

If you run your program now you'll probably see something; but probably not what you want. You'd expect your rounded rectangle to cover the entire view!

This isn’t happening because after you insert the RoundedRectangle, the width of the View is changed to the width you set in the interface. This is because the properties you assigned in your interface file are actually set after the canvas is initialised (don't worry, there's good reasoning behind this, but it is annoying).

If you View changes size later your RoundedRectangle will stay it's original size (which is probably 1 x 1). So how do we fix this?

Updating the Rounded Rectangle's Size

To solve the issue encountered in previous section we need to change the size of the RoundedRectangle whenever the size of the View changes.

Fortunately Silica has a very way to solve this. However, you first need to assign your RoundedRectangle to a property so you can access it in other functions.

Assign the Rounded Rectangle to a Property

First, define it in the properties table.

class "CustomView" extends "View" {

    roundedRectangleObject = false;
    
}

Then assign the RoundedRectangle instance you created to the property in the :initialiseCanvas function.

roundedRectangleObject = roundedRectangleObject

Updating the Size

When x, y, width or height of your View changes it will call :updateWidth (or the equivalent depending upon what property changed).

As your RoundedRectangle only need to update its size, add the :updateWidth and :updateWidth functions.

function CustomView:updateWidth( width )
    self.roundedRectangle.width = width
end

function CustomView:updateHeight( height )
    self.roundedRectangle.height = height
end

You can, of course, do whatever you like with the value it gives you (such as divide it by two to make it only cover half the view).

If you run your program again you should see your RoundedRectangle in all its glory! Whenever your View resizes the RoundedRectangle will always cover it.

Your rounded rectangle!

Conclusion: Adding Basics Shapes to a View

You should now have a good understanding of to how to:

What you should try now:

  • Look at the Drawing page at the other shapes you can use, such as Circle, and try to achieve the same result.
  • Read the next section, which explains how to assign non-theme based values to GraphicsObject subclasses, such as the Text object.

Adding Text to a View

Text is added to a View using a GraphicsObject subclass called Text. The process is almost identical to the process used in the Adding Basics Shapes to a View section. The main difference is you have to set the text of the Text object and update it when the changes.

Follow the same process that was used to create a RoundedRectangle, but instead of RoundedRectangle use a Text object. You don't need to connect your theme to the cornerRadius and fillColour properties, but you will need connect to the textColour property. You will need to keep the :updateWidth and :updateHeight functions because the Text object will clip its contents (i.e., the text) if not large enough.

Testing Time

If you run your program it should open error free, but nothing will be visible. This is because you haven't actually set the text of the Text object yet.

If something isn't working, try to figure out what's wrong based on the error and look back over the previous instructions and ensure that you followed the RoundedRectangle instructions correctly. If you're still lost Get Help.

Adding the text Property

Give your View subclass a new property called text. This property will work just like it does in View subclasses like Button and Label. When you set the text property, either in code or using an interface file, it will automatically change the text that is shown.

If you want your View subclass to have a default value set the property value in the properties table to that value. In this case, the default will be "Hello".

class "CustomView" extends "View" {

    textObject = false;
    text = "Hello";

}

Setting the Text

Now that there is a property called text you can create a setter which will call code whenever the text property is changed, including when the View is loaded from an interface file.

Simply set the text property of the Text object to the new value of the text property.

function CustomView:setText( text )
    self.text = text
    self.textObject.text = text
end

Reopen your program and voilà! You should see your text appear. If you set the text property of your an instance of your subclass the text should automatically change.

Note If the text is cut off or you can't see it there's a strong chance your view is too small or the Text object is to small. If you want your View to automatically adjust its size based on the size of the text look at the Advance Concept – Autosizing page.

Text!!!