-
Notifications
You must be signed in to change notification settings - Fork 238
The Element
The element
is the class that deals with the graphic representation of fine-grained elements inside a window which may be static graphics or active controls. Elements are light-weight objects. By design, they are minimal in terms of memory footprint. For example, elements do not have information about their coordinates. That means that the client has to supply the information on where an element is drawn. That is done using context
parameters that most of the element's member functions take. For example:
virtual void draw(context const& ctx);
The draw
interface above implies that you need to supply some "context" in order to draw an element. The context
class provides information such as the element's bounds
, a rectangle that indicates where the element needs to be drawn. The context
class inherits from the basic_context
, a struct which contains a reference to the view
—the main content view inside a window where elements are placed, and a canvas
, a drawing surface where the elements are drawn.
struct basic_context
{
/*...*/
elements::view& view;
elements::canvas& canvas;
};
The context
class inherits from basic_context
and additionally includes a pointer to the "current" element being drawn, a pointer to its parent element in the elements hierarchy (that which contains it), and the element's bounds
, a rectangle that indicates where the element needs to be drawn.
Only one element
member function takes in a basic_context
argument, the limits
member function (more on that later). Other than that, all the other element
member functions take in a context
argument. The reason for this is that the at the point where the limits
is called, the element's position and parent-child relationships in the elements hierarchy have not been established yet.
class context : public basic_context
{
public:
/*...*/
element* element;
context const* parent;
rect bounds;
};
class element : public std::enable_shared_from_this<element>
{
public:
using element_ptr = std::shared_ptr<element>;
using element_const_ptr = std::shared_ptr<element const>;
/*...*/
// Image
virtual view_limits limits(basic_context const& ctx) const;
virtual view_stretch stretch() const;
virtual element* hit_test(context const& ctx, point p);
virtual void draw(context const& ctx);
virtual void layout(context const& ctx);
virtual void refresh(context const& ctx, element& element, int outward = 0);
void refresh(context const& ctx, int outward = 0);
// Control
virtual element* click(context const& ctx, mouse_button btn);
virtual void drag(context const& ctx, mouse_button btn);
virtual bool key(context const& ctx, key_info k);
virtual bool text(context const& ctx, text_info info);
virtual bool cursor(context const& ctx, point p, cursor_tracking status);
virtual bool scroll(context const& ctx, point dir, point p);
virtual bool focus(focus_request r);
virtual element const* focus() const;
virtual element* focus();
virtual bool is_control() const;
// Receiver
virtual void value(bool val);
virtual void value(int val);
virtual void value(double val);
virtual void value(std::string_view val);
};
These member functions pertain to how an element is drawn as well as providing hints on how an element wants to be laid out.
Determines the minimum and maximum extents of an element.
virtual view_limits limits(basic_context const& ctx) const;
view_limits
is a struct containing the minimum and maximum information.
struct view_limits
{
point min;
point max;
};
An element has a fixed size if min == max
. An element is infinitely resizable if max == full_extent
, where full_extent
is a constexpr
defined by the library.
Determines how much an element can stretch horizontally and vertically relative to other elements in a column or row. If a row (or column) has more space allocated to it than the minimum required by all elements in that row (or column), as specified by the sum of all the minimum limits of each of the elements, then the resizable elements (see limits
above) are stretched to fit the allocated space. The value returned by stretch
dictates how much that element can stretch. The default is 1.0. A stretch value of 2.0 means that the element is twice as stretchable compared to all other elements in that row (or column), assuming the others have the default 1.0 stretch value.
virtual view_stretch stretch() const;
view_stretch
is a struct containing the stretchiness information.
struct view_stretch
{
float x;
float y;
};
Returns a pointer to the inner-most element that intersects point p
or nullptr
if none.
virtual element* hit_test(context const& ctx, point p);
Draw the element. The context's bounds
member specifies where the element should be drawn. The element should use the context's canvas member for drawing. The draw
member function should not be called directly or indirectly from other member functions, except from a parent's draw
function, drawing its children. If you need to redraw an element from other member functions, call the refresh
member function instead.
virtual void draw(context const& ctx);
As mentioned, by design, elements do not know about their location in the window. However, some elements may make use of the layout
member function if it needs to update information necessary for drawing. For example, composite elements, those that have children elements, may cache information about the coordinates of sub elements. The layout
member function is called, prior to calling draw
, to give the element a chance to prepare its children for drawing. The context's bounds
member specifies the outermost bounds of the element. The composite element subdivides this area as appropriate for each of its children.
virtual void layout(context const& ctx);
Send a refresh request for a particular element to redraw it. This function essentially invalidates the bounds of the element and triggers a subsequent redraw of that area in the window. The outward
argument specifies which of the element's parents should be refreshed. If for example, a margin element encloses an image element, and you want to refresh the image, including its margin, then specify an outward
argument of 1. The default outward
argument is 0.
virtual void refresh(context const& ctx, element& element, int outward = 0);
void refresh(context const& ctx, int outward = 0);
The second refresh
member function simply forwards to the first, passing in a reference to this
element.
void element::refresh(context const& ctx, int outward = 0)
{
refresh(ctx, *this, outward);
}
Respond to a mouse button event. Returns the innermost element being clicked or nullptr
if none. The innermost element is the one that actually acts on the click event.
virtual element* click(context const& ctx, mouse_button btn);
mouse_button
is a struct that provides the relevant information.
struct mouse_button
{
enum what { left, middle, right };
bool down;
int num_clicks;
what state;
int modifiers;
point pos;
};
- down: True if the mouse button is pressed, otherwise it is released.
- num_clicks: The number of clicks (single click, double click, etc.)
- state: Indicates whether the left, middle or right button is clicked.
- modifiers: A bit mask that specifies which relevant keys are being pressed at the same time (see below).
- pos: The current mouse position
The modifiers
member is a bit mask with these possible values:
enum : uint16_t
{
mod_shift = 0x0001,
mod_control = 0x0002,
// mod_alt maps to the Alt key on PC keyboards
// and maps to the Option key on MacOS
mod_alt = 0x0004,
// mod_super maps to the Windows key on PC keyboards
// and maps to the Command key on MacOS
mod_super = 0x0008,
// mod_action maps to mod_control on Windows and Linux
// and maps to mod_super on MacOS
mod_action = 0x0010,
};
This member function is called after a mouse click, when the user drags the mouse. This will be called continuously while the mouse is being dragged.
virtual void drag(context const& ctx, mouse_button btn);
See mouse_button
above for information about the btn
argument.
Handle raw key events.
virtual bool key(context const& ctx, key_info k);
key_info
is a struct that provides the relevant information about the key event:
struct key_info
{
key_code key;
key_action action;
int modifiers;
};
key_code
are enumerations about the pressed key:
enum class key_code : int16_t
{
unknown = -1,
// Printable keys
space = 32,
apostrophe = 39, // '
comma = 44, // ,
minus = 45, // -
period = 46, // .
slash = 47, // /
_0 = 48,
_1 = 49,
_2 = 50,
_3 = 51,
_4 = 52,
_5 = 53,
_6 = 54,
_7 = 55,
_8 = 56,
_9 = 57,
semicolon = 59, // ;
equal = 61, // =
a = 65,
b = 66,
c = 67,
d = 68,
e = 69,
f = 70,
g = 71,
h = 72,
i = 73,
j = 74,
k = 75,
l = 76,
m = 77,
n = 78,
o = 79,
p = 80,
q = 81,
r = 82,
s = 83,
t = 84,
u = 85,
v = 86,
w = 87,
x = 88,
y = 89,
z = 90,
left_bracket = 91, // [
backslash = 92, // \ (back-slash)
right_bracket = 93, // ]
grave_accent = 96, // `
world_1 = 161, // non-US #1
world_2 = 162, // non-US #2
// Function keys
escape = 256,
enter = 257,
tab = 258,
backspace = 259,
insert = 260,
_delete = 261,
right = 262,
left = 263,
down = 264,
up = 265,
page_up = 266,
page_down = 267,
home = 268,
end = 269,
caps_lock = 280,
scroll_lock = 281,
num_lock = 282,
print_screen = 283,
pause = 284,
f1 = 290,
f2 = 291,
f3 = 292,
f4 = 293,
f5 = 294,
f6 = 295,
f7 = 296,
f8 = 297,
f9 = 298,
f10 = 299,
f11 = 300,
f12 = 301,
f13 = 302,
f14 = 303,
f15 = 304,
f16 = 305,
f17 = 306,
f18 = 307,
f19 = 308,
f20 = 309,
f21 = 310,
f22 = 311,
f23 = 312,
f24 = 313,
f25 = 314,
kp_0 = 320,
kp_1 = 321,
kp_2 = 322,
kp_3 = 323,
kp_4 = 324,
kp_5 = 325,
kp_6 = 326,
kp_7 = 327,
kp_8 = 328,
kp_9 = 329,
kp_decimal = 330,
kp_divide = 331,
kp_multiply = 332,
kp_subtract = 333,
kp_add = 334,
kp_enter = 335,
kp_equal = 336,
left_shift = 340,
left_control = 341,
left_alt = 342,
left_super = 343,
right_shift = 344,
right_control = 345,
right_alt = 346,
right_super = 347,
menu = 348
};
key_action
is another enumeration about the state of the key, whether it is being pressed, released or repeatedly pressed:
enum class key_action
{
unknown = -1,
release = 0,
press = 1,
repeat = 2,
};
modifiers
is the same as that for click
: a bit mask with these possible values:
enum : uint16_t
{
mod_shift = 0x0001,
mod_control = 0x0002,
// mod_alt maps to the Alt key on PC keyboards
// and maps to the Option key on MacOS
mod_alt = 0x0004,
// mod_super maps to the Windows key on PC keyboards
// and maps to the Command key on MacOS
mod_super = 0x0008,
// mod_action maps to mod_control on Windows and Linux
// and maps to mod_super on MacOS
mod_action = 0x0010,
};
Returns true
if the element handled the key event.
While key
handles raw key events, text
handles processed text entry, possibly managing multiple key presses and multi-language inputs. The prepared text information is UCS4 code point.
virtual bool text(context const& ctx, text_info info);
text_info
is a struct that contains the UCS4 code point and the same modifier bit mask as that of key
(see key
above):
struct text_info
{
uint32_t codepoint;
int modifiers;
};
Returns true
if the element handled the text entry.
Handles cursor move events. This is called when the cursor hovers over an element.
virtual bool cursor(context const& ctx, point p, cursor_tracking status);
The point, p
, specifies where the cursor position is. cursor_tracking
is an enumeration that tells the element if the cursor is either entering, hovering, or leaving the element:
enum class cursor_tracking
{
entering, // Sent when the cursor is entering the element
hovering, // Sent when the cursor is hovering over the element
leaving // Sent when the cursor is leaving the element
};
Returns true
if the element handled the cursor event.
Handle scroll-wheel or scroll gesture events.
virtual bool scroll(context const& ctx, point dir, point p);
The point, p
, specifies where the cursor position is. The point, dir
, specifies the horizontal (x) and vertical (y) direction, how many pixels to move in the x and y axis. Negative means go up or left. positive means go down or right. Scrolling can be fractional for HDPI and also to allow inertial scrolling.
Returns true
if the element handled the scroll event.
Some elements such as text entry boxes can become the focus. When an element becomes the focus, it will be the one receiving key
and text
inputs.
virtual bool focus(focus_request r);
virtual element const* focus() const;
virtual element* focus();
Elements wanting to be focus should implement the first focus
overload. Focus negoiations proceeds as follows:
- The element is asked if it can become the focus (
wants_focus
). - The element begins focus (
begin_focus
). - When the client clicks on something else that also wants to become focus, the current focus is asked to end focus (
end_focus
).
This is specified in the focus_request
enumeration below:
enum class focus_request
{
wants_focus,
begin_focus,
end_focus
};
The second and third focus
overrides return the current focus. This is typically implemented by composite elements.
Return true
if the element is a control element. A control element is an element that requires user interaction and implements one or more of the element control member functions.
virtual bool is_control() const;
Elements such as buttons and sliders may have values. These member functions respond to value changes. For example, a slider accepts normalized values from 0.0 to 1.0. Assigning the slider a value of 0.5 will move its thumb to the middle position. Doing an element refresh
(see refresh
member function above) will ask elements to redraw the slider with the updated position.
virtual void value(bool val);
virtual void value(int val);
virtual void value(double val);
virtual void value(std::string_view val);