CANVAS
The CANVAS element lets you draw your own shapes using the Kt::Graphics scripting API.
How else to add graphics?
There are several ways to add graphics in KUIML: you can load external images from files using IMAGE tag (and via multiple IMAGE_... related controls), insert built-in svg directly into KUIML using svg tag, for simple rectangular objects you can use WIDGET or TEXT with background_color attribute.
For static graphics (like icons, etc) it's usually easier to use inline svg (which, by the way, also can be changed on the fly). For custom elements that should be frequently redrawn (like custom meters, rotating knobs, faders, graphs etc) you can either use pre-rendeded PNG/SVG stripes with one of the IMAGE_... controls, or draw them inside KUIML with CANVAS.
Attributes brief detailed show all inherited
Name | Description | Default | |||
---|---|---|---|---|---|
render_script | inline script that draws onto the current context using Graphics API | empty | |||
requires | List of objects expected by the script | empty | |||
script_handles_opacity | if true, the render script should handle the opacity of the widget | false | |||
Methods | |||||
Invalidate | Call this method to redraw the canvas (on value changes or on timer) | ||||
Inherited from widgets | |||||
opacity | Opacity of the widget | 100% | |||
visible | Show/hide the widget, does not impact the layout, as opposed to the cell's display attribute | true | |||
enabled | Enable/disable the widget. When disabled, the widget does not react to mouse or keyboard events. it is displayed but inactive. | true | |||
cursor | Cursor to display when the mouse is over the active part of the widget | System... | |||
mouse_sensitive | Enables or disables mouse control on widgets | Dependi... | |||
auto_focus | Enables or disables auto focus | Dependi... | |||
handled_keys | List of keys handled by the widget, which will trigger key_pressed and key_released events | empty | |||
default_keys_enabled | Enable or disable the default keys handled natively by the widget. | true | |||
buffered | Enable or disable buffering of the widget. | false | |||
transparent | Enable or disable transparency for the widget. | false | |||
Drag and Drop Attributes | |||||
drag_source_ids | List of objects to use as drag sources for drag and drop operations (each one may have a different applicative type) | empty | |||
drag_applicative_types | List of applicative types to use for each drag source defined by the drag_source_ids list | empty | |||
drag_operations | List of drag operations allowed. 'copy' or 'move' operations are currently supported | empty | |||
drag_context | Optional string to define the serialization context used for the drag operation | empty | |||
drag_enabled | Enable drag operations on the widget | true | |||
drop_target_ids | List of objects to use as drop targets for drag and drop operations (each one may have a different applicative type) | empty | |||
drop_applicative_types | List of applicative types to use for each drop target defined by the drop_target_ids list. | empty | |||
drop_file_types | List of supported file types to use for each drop target defined by the drop_target_ids list. Each wild card may contain several extensions, separated by ','. For example, with "*.*;*.doc,*.txt", the first drop target will accept all files, and the second one will accept only .doc and .txt files | empty | |||
dropped_file_path_string_ids | List of string objects than can receive the path of a file that was dropped, for each drop target defined by the drop_target_ids list | empty | |||
drop_context | Optional string to define the deserialization context used for the drop operation | empty | |||
drop_enabled | Enable drop operations on the widget | true | |||
Exposed Attributes And Events (read-only) | |||||
mouse_over | Set to true when the mouse is over the active area of the widget | false | |||
mouse_down | Bit field containing the state of mouse buttons: first bit is left button, second is right and third is mid button. When no button is pushed, the value of mouse_down is zero | 0 | |||
mouse_position.x | Horizontal position of the mouse in the widget (relative to the upper left corner of the widget) | ||||
mouse_position.y | Vertical position of the mouse in the widget (relative to the upper left corner of the widget) | ||||
mouse_screen_position.x | Absolute horizontal position of the mouse on the screen | ||||
mouse_screen_position.y | Absolute vertical position of the mouse on the screen | ||||
mouse_wheel_up | Event fired when a mouse wheel is turned up | ||||
mouse_wheel_down | Event fired when a mouse wheel is turned up | ||||
focus | Set to true when the widget has keyboard focus and receives keyboard events | false | |||
key_pressed | Event fired when a key (filtered by the handled_key list) is pressed | ||||
key_pressed.key | last virtual key that was pressed | ||||
key_pressed.characters | Character(s) corresponding to the last pressed key, if it was a character key (key_pressed.key=0) | ||||
key_released | Event fired when a key (filtered by the handled_key list) is released | ||||
key_released.key | last virtual key that was released | ||||
key_released.characters | Character(s) corresponding to the last released key, if it was a character key (key_released.key=0) | ||||
dragging | Set to true when a dragging operation is in progress for this widget | false | |||
drag_operation | type of the current drag operation (None, Copy, Move) | enumera... | |||
dropping | Set to true when a dropping operation is in progress for this widget | false | |||
drop_operation | type of the current drop operation (None, Copy, Move) | enumera... | |||
Inherited from cell | |||||
Properties Relative to Parent Cell | |||||
v_align | Vertical alignment of the cell in its parent cell | center | |||
h_align | Horizontal alignment of the cell in its parent cell | center | |||
h_position | Horizontal position of the cell in its parent cell | ||||
v_position | Vertical position of the cell in its parent cell | ||||
h_offset | horizontal offset of the cell from the position computed by the layout engine | 0 | |||
v_offset | vertical offset of the cell from the position computed by the layout engine | 0 | |||
flex | flexibility of the cell | 0 | |||
display | include/exclude the cell from the layout | true | |||
Cell Properties | |||||
layout_type | Selects a layout type: row, column or layer_stack | column | |||
width | Forced width of the cell | ||||
min_width | minimum width allowed for the cell | ||||
max_width | maximum width allowed for the cell | ||||
height | Forced height of the cell | ||||
min_height | minimum height allowed for the cell | ||||
max_height | maximum height allowed for the cell | ||||
margin | margin of the cell | 0 | |||
h_margin | Horizontal margin of the cell | 0 | |||
v_margin | Vertical margin of the cell | 0 | |||
spacing | Space between children cells | 0 | |||
spacing_flex | Flex value to allocate extra space between children cells | 0 | |||
internal_v_align | Vertical alignment of children cells group inside the cell | center | |||
internal_h_align | Horizontal alignment of children cells group inside the cell | center | |||
reflow | when set to true, the layout engine will treat chidren cell as reflowable elements. For a fixed height column, the width is computed accordingly, and for a fixed width row, the height is computed accordingly | false | |||
flip | display children cells in reverse order (right to left or bottom to top) | false | |||
Inheritable properties: these text properties are inherited by children cells (and can be overridden) | |||||
font_face | Name of the font for text contained in the cell and sub cells | empty | |||
font_size | Size of the font for any text contained in the cell | System... | |||
font_size_mode | Defines how font size is computed to select the right font. | cell | |||
font_weight | Weight of the font for any text contained in the cell | System... | |||
font_style | Style of the font for any text contained in the cell | System... | |||
font_quality | Quality of the font | System... | |||
font_escapement | Escapement (angle in degrees) of the font | 0 | |||
text_decoration | Decoration of the text for any text contained in the cell | System... | |||
text_color | Color of any text contained in the cell | System... | |||
Common attributes | |||||
id | Identifier of the element | empty |
Canvas drawing example
- <CANVAS id="shapes"
- width="300" height="300" opacity=".8" render_script="
- Kt::Graphics::Context@ ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- double w = this.width;
- double h = this.height;
- /* scale to virtual coordinates from -1 to 1 */
- ctx.transform.Scale(w/2,h/2);
- ctx.transform.Translate(1,1);
- /* set color in RGB (0 to 1 values) */
- double r = 0.2;
- double g = 0.4;
- double b = 0.6;
- ctx.source.SetRGBA(r,g,b,1);
- /* calculate nice line width for that virtual coords */
- double lineW=3*2/sqrt(w*w+h*h);
- ctx.settings.lineWidth=lineW;
- /* start new path */
- ctx.path.Clear();
- /* add three crossed lines */
- ctx.path.MoveTo(-.7,-.7);
- ctx.path.LineTo(.7,.7);
- ctx.path.MoveTo(.7,-.7);
- ctx.path.LineTo(-.7,.7);
- ctx.path.MoveTo(-1,0);
- ctx.path.LineTo(1,0);
- /* add a circle */
- ctx.path.NewSubPath();
- ctx.path.Arc(0, 0, 1-lineW/2 , 0, 360);
- /* now stroke (draw) the path */
- ctx.StrokePath();
- /* add radial gradient */
- Kt::Graphics::GradientDrawPattern@ gradient=ctx.patterns.NewRadialGradient(0,0,0,0,0,1);
- gradient.AddColorStopRGBA(0,r,g,b,0);
- gradient.AddColorStopRGBA(1,r,g,b,0.9);
- gradient.SelectAsSource();
- ctx.FillPath();
- /* move to center */
- ctx.path.MoveTo(0,0);
- /* scale back */
- ctx.transform.Scale(2/w,2/w);
- /* now add text */
- ctx.path.Clear();
- Kt::Graphics::DrawPattern@ rgba=ctx.patterns.NewRGBA(1,0.9,0.6,1);
- rgba.SelectAsSource();
- ctx.font.SelectFontFace("Georgia");
- ctx.font.SetFontSize(70);
- ctx.path.Text("ABC");
- /* fill text */
- ctx.FillPath();
- /* add a borderline (stroke) for text */
- ctx.source.SetRGBA(0,0,0,0.5);
- ctx.settings.lineWidth=1;
- ctx.StrokePath();
- }" requires="shapes.width;shapes.height" />
How to make a simple slider using CANVAS
To demonstrate how you can create your own controls we'll create a simple "volume" slider.
First we create a PARAM that keeps the volume value and add a simple PARAM_TEXT_CONTROL to see and control it.
- <PARAM id="volume" min="0" max="100" default="50" unit="%" />
- <PARAM_TEXT_CONTROL param_id="volume" />
Now let's add the CANVAS element that draws a simple rectangle according to the volume level, and add an ACTION_TRIGGER that makes the CANVAS redraw each time the volume value changes.
- <!-- draw on a canvas -->
- <CANVAS id="slider_canvas"
- width="200" height="30" opacity="1" render_script="
- /* convert parameter value to 0..1 range */
- double param_normalized = (volume-volume.min)/(volume.max-volume.min);
- /* get canvas width and height */
- double w = this.width;
- double h = this.height;
- /* get the context object to draw */
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- /* draw a rectangle with calculated width */
- ctx.source.SetRGB(0.29, 0.701, 0.956);
- ctx.path.Clear();
- ctx.path.Rectangle(0, 0, w*param_normalized, h/2);
- ctx.FillPath();
- }
- " requires="slider_canvas.*;volume.*" />
- <!-- redraw the canvas on parameter changes -->
- <ACTION_TRIGGER event_id="volume.value_changed" script="slider_canvas.Invalidate()" requires="slider_canvas.Invalidate" />
We can wrap the CANVAS element into a LAYER_STACK and add an INVISIBLE_PARAM_SLIDER on top of it to make it react to mouse events.
- <!-- a param to control volume -->
- <PARAM id="volume" min="0" max="100" default="50" unit="%" />
- <!-- put children elements in a row -->
- <ROW>
- <!-- put children layers on top of each other -->
- <!-- draw on a canvas -->
- <CANVAS id="slider_canvas"
- width="200" height="30" opacity="1" render_script="
- /* convert parameter value to 0..1 range */
- double param_normalized = (volume-volume.min)/(volume.max-volume.min);
- /* get canvas width and height */
- double w = this.width;
- double h = this.height;
- /* get the context object to draw */
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- /* draw a rectangle with calculated width */
- ctx.source.SetRGB(0.29, 0.701, 0.956);
- ctx.path.Clear();
- ctx.path.Rectangle(0, 0, w*param_normalized, h);
- ctx.FillPath();
- }
- " requires="slider_canvas.*;volume.*" />
- <!-- an invisible slider to control the volume -->
- <INVISIBLE_PARAM_SLIDER param_id="volume" width="100%" height="100%" orientation="horizontal" />
- </LAYER_STACK>
- <!-- a simple control for param -->
- <PARAM_TEXT_CONTROL param_id="volume" width="65"/>
- </ROW>
- <!-- redraw the canvas on parameter changes -->
- <ACTION_TRIGGER event_id="volume.value_changed" script="slider_canvas.Invalidate()" requires="slider_canvas.Invalidate" />
Let's make slider a little more interesting by adding several bars that are clipped by the rectangular area.
- <!-- a param to control volume -->
- <PARAM id="volume" min="0" max="100" default="50" unit="%" />
- <!-- put children elements in a row -->
- <ROW>
- <!-- put children layers on top of each other -->
- <!-- draw on a canvas -->
- <CANVAS id="slider_canvas"
- width="200" height="30" opacity="1" render_script="
- /* convert parameter value to 0..1 range */
- double param_normalized = (volume-volume.min)/(volume.max-volume.min);
- /* get canvas width and height */
- double w = this.width;
- double h = this.height;
- /* get the context object to draw */
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- /* create a rectangle with calculated width */
- ctx.source.SetRGB(0.29, 0.701, 0.956);
- ctx.path.Clear();
- ctx.path.Rectangle(0, 0, w*param_normalized, h);
- /* make this a clip area, so future drawing is clipped */
- ctx.ClipWithPath();
- /* make several bars of different height */
- ctx.path.Clear();
- double columns = 16;
- double colw = (w/columns)*0.7;
- for (int n=0;n<columns;n++) {
- double colx = n*(w/columns);
- double colh = h*((n+1)/columns);
- ctx.path.Rectangle(colx, h-colh, colw, colh);
- }
- ctx.FillPath();
- }
- " requires="slider_canvas.*;volume.*" />
- <!-- an invisible slider to control the volume -->
- <INVISIBLE_PARAM_SLIDER param_id="volume" width="100%" height="100%" orientation="horizontal" />
- </LAYER_STACK>
- <!-- a simple control for param -->
- <PARAM_TEXT_CONTROL param_id="volume" width="65"/>
- </ROW>
- <!-- redraw the canvas on parameter changes -->
- <ACTION_TRIGGER event_id="volume.value_changed" script="slider_canvas.Invalidate()" requires="slider_canvas.Invalidate" />
We can paint with a gradient instead of a solid flat color.
- <!-- a param to control volume -->
- <PARAM id="volume" min="0" max="100" default="100" unit="%" />
- <!-- put children elements in a row -->
- <ROW>
- <!-- put children layers on top of each other -->
- <!-- draw on a canvas -->
- <CANVAS id="slider_canvas"
- width="200" height="30" opacity="1" render_script="
- /* convert parameter value to 0..1 range */
- double param_normalized = (volume-volume.min)/(volume.max-volume.min);
- /* get canvas width and height */
- double w = this.width;
- double h = this.height;
- /* get the context object to draw */
- auto ctx = Kt::Graphics::GetCurrentContext();
- if (@ctx != null) {
- /* create a rectangle with calculated width */
- ctx.source.SetRGB(0.29, 0.701, 0.956);
- ctx.path.Clear();
- ctx.path.Rectangle(0, 0, w*param_normalized, h);
- /* make this a clip area, so future drawing is clipped */
- ctx.ClipWithPath();
- /* make several bars of different height */
- ctx.path.Clear();
- double columns = 16;
- double colw = (w/columns)*0.7;
- for (int n=0;n<columns;n++) {
- double colx = n*(w/columns);
- double colh = h*((n+1)/columns);
- ctx.path.Rectangle(colx, h-colh, colw, colh);
- }
- /* add a colorful gradient */
- auto gradient = ctx.patterns.NewLinearGradient(0,0,w,0);
- gradient.AddColorStopRGBA(0, .5,.76,.52, 1);
- gradient.AddColorStopRGBA(0.55, .5,.76,.52, 1);
- gradient.AddColorStopRGBA(0.63, 1,0.86,0, 1);
- gradient.AddColorStopRGBA(0.75, 1,0.86,0, 1);
- gradient.AddColorStopRGBA(1, 0.92,0.33,0.18, 1);
- gradient.SelectAsSource();
- ctx.FillPath();
- }
- " requires="slider_canvas.*;volume.*" />
- <!-- an invisible slider to control the volume -->
- <INVISIBLE_PARAM_SLIDER param_id="volume" width="100%" height="100%" orientation="horizontal" />
- </LAYER_STACK>
- <!-- a simple control for param -->
- <PARAM_TEXT_CONTROL param_id="volume" width="65"/>
- </ROW>
- <!-- redraw the canvas on parameter changes -->
- <ACTION_TRIGGER event_id="volume.value_changed" script="slider_canvas.Invalidate()" requires="slider_canvas.Invalidate" />
If you want to make this slider reusable you can wrap into the TEMPLATE element, like shown in this demo: How to make simple knobs.
Find more drawing examples in CANVAS Graphics API.