Merge branch 'c3' of https://git.alemauri.eu/alema/ugui into c3
This commit is contained in:
commit
eb62e9ad72
17
TODO
17
TODO
@ -4,12 +4,11 @@
|
|||||||
[x] Implement div.view and scrollbars
|
[x] Implement div.view and scrollbars
|
||||||
[x] Port font system from C to C3 (rewrite1)
|
[x] Port font system from C to C3 (rewrite1)
|
||||||
[ ] Update ARCHITECTURE.md
|
[ ] Update ARCHITECTURE.md
|
||||||
[ ] Write a README.md
|
[x] Write a README.md
|
||||||
[x] Use an arena allocator for cache
|
[x] Use an arena allocator for cache
|
||||||
[ ] Do not redraw if there was no update (no layout and no draw)
|
[ ] Do not redraw if there was no update (no layout and no draw)
|
||||||
[ ] Do command buffer damage tracking based on a context grid (see rxi writeup)
|
[ ] Do command buffer damage tracking based on a context grid (see rxi writeup)
|
||||||
[x] Better handling of the active and focused widgets, try
|
[x] Better handling of the active and focused widgets, try to maintain focus until mouse release (fix scroll bars)
|
||||||
to maintain focus until mouse release (fix scroll bars)
|
|
||||||
[x] Clip element bounds to parent div, specifically text
|
[x] Clip element bounds to parent div, specifically text
|
||||||
[ ] Resizeable divs
|
[ ] Resizeable divs
|
||||||
[x] Implement a z index and sort command buffer based on that
|
[x] Implement a z index and sort command buffer based on that
|
||||||
@ -30,14 +29,14 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[ ] .png
|
[ ] .png
|
||||||
[ ] .jpg
|
[ ] .jpg
|
||||||
[ ] gif support?
|
[ ] gif support?
|
||||||
[ ] layout_set_max_rows() and layout_set_max_columns()
|
[x] layout_set_max_rows() and layout_set_max_columns()
|
||||||
[x] Maybe SDF sprites??
|
[x] Maybe SDF sprites??
|
||||||
[x] Stylesheets and stylesheet import
|
[x] Stylesheets and stylesheet import
|
||||||
[x] use SDF to draw anti-aliased rounded rectangles https://zed.dev/blog/videogame
|
[x] use SDF to draw anti-aliased rounded rectangles https://zed.dev/blog/videogame
|
||||||
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
||||||
internal use functions (used to create widgets)
|
internal use functions (used to create widgets)
|
||||||
[x] The render loop RAPES the gpu, valve pls fix
|
[x] The render loop RAPES the gpu, valve pls fix
|
||||||
[ ] The way the element structures are implemented wastes a lot of memory since
|
[x] The way the element structures are implemented wastes a lot of memory since
|
||||||
each struct Elem, struct Cmd, etc. is as big as the largest element. It would
|
each struct Elem, struct Cmd, etc. is as big as the largest element. It would
|
||||||
be better to use a different allcation strategy.
|
be better to use a different allcation strategy.
|
||||||
[ ] Add a way to handle time events like double clicks
|
[ ] Add a way to handle time events like double clicks
|
||||||
@ -52,7 +51,7 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[x] Center elements to the row/column
|
[x] Center elements to the row/column
|
||||||
[x] Text wrapping / reflow
|
[x] Text wrapping / reflow
|
||||||
[x] Implement a better and unified way to place a glyph and get the cursor position, maybe with a struct
|
[x] Implement a better and unified way to place a glyph and get the cursor position, maybe with a struct
|
||||||
[ ] Correct whitespace handling in text (\t \r etc)
|
[x] Correct whitespace handling in text (\t \r etc)
|
||||||
[x] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay)
|
[x] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay)
|
||||||
instead of the curren multi-frame approach.
|
instead of the curren multi-frame approach.
|
||||||
[x] Implement column/row sizing (min, max)
|
[x] Implement column/row sizing (min, max)
|
||||||
@ -79,8 +78,8 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[x] Mouse scroll wheel
|
[x] Mouse scroll wheel
|
||||||
[ ] Touch input
|
[ ] Touch input
|
||||||
[x] Do not set input event to true if the movement was zero (like no mouse movement)
|
[x] Do not set input event to true if the movement was zero (like no mouse movement)
|
||||||
[ ] Use input event flags, for example to consume the input event
|
[x] Use input event flags, for example to consume the input event
|
||||||
[ ] Fix bug in text box: when spamming keys you can get multiple characters in the text input field
|
[x] Fix bug in text box: when spamming keys you can get multiple characters in the text input field
|
||||||
of the context, this causes a bug where only the first char is actually used
|
of the context, this causes a bug where only the first char is actually used
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
@ -89,7 +88,7 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
- border width
|
- border width
|
||||||
- border radius
|
- border radius
|
||||||
[x] add a command to update an atlas
|
[x] add a command to update an atlas
|
||||||
[ ] New window command, useful for popups
|
[x] New window command, useful for popups
|
||||||
[x] Text command returns the text bounds, this way we can avoid the pattern
|
[x] Text command returns the text bounds, this way we can avoid the pattern
|
||||||
draw_text(a, pos) -> off = compute_bounds(a) -> draw_text(b, pos+off) -> ...
|
draw_text(a, pos) -> off = compute_bounds(a) -> draw_text(b, pos+off) -> ...
|
||||||
[ ] Rounded rectangle with different radius for each corner
|
[ ] Rounded rectangle with different radius for each corner
|
||||||
|
@ -34,7 +34,8 @@ enum ElemType {
|
|||||||
|
|
||||||
bitstruct ElemFlags : uint {
|
bitstruct ElemFlags : uint {
|
||||||
bool updated : 0;
|
bool updated : 0;
|
||||||
bool is_new : 1;
|
bool is_new : 1; // element is new in the cache
|
||||||
|
bool shown : 2; // element has been shown (drawn) this frame
|
||||||
}
|
}
|
||||||
|
|
||||||
bitstruct ElemEvents : uint {
|
bitstruct ElemEvents : uint {
|
||||||
@ -89,20 +90,8 @@ alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS};
|
|||||||
|
|
||||||
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, WRONG_ELEMENT_TYPE, WRONG_ID;
|
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, WRONG_ELEMENT_TYPE, WRONG_ID;
|
||||||
|
|
||||||
struct Ctx {
|
struct InputData {
|
||||||
IdTree tree;
|
|
||||||
ElemCache cache;
|
|
||||||
CmdQueue cmd_queue;
|
|
||||||
StyleMap styles;
|
|
||||||
// total size in pixels of the context
|
|
||||||
ushort width, height;
|
|
||||||
Font font;
|
|
||||||
SpriteAtlas sprite_atlas;
|
|
||||||
|
|
||||||
bool has_focus;
|
|
||||||
struct input {
|
|
||||||
InputEvents events;
|
InputEvents events;
|
||||||
int z_index; // the layer the input events need to be propageted to
|
|
||||||
struct mouse {
|
struct mouse {
|
||||||
Point pos, delta;
|
Point pos, delta;
|
||||||
// mouse_down: bitmap of mouse buttons that are held
|
// mouse_down: bitmap of mouse buttons that are held
|
||||||
@ -121,6 +110,19 @@ struct Ctx {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Ctx {
|
||||||
|
IdTree tree;
|
||||||
|
ElemCache cache;
|
||||||
|
CmdQueue cmd_queue;
|
||||||
|
StyleMap styles;
|
||||||
|
// total size in pixels of the context
|
||||||
|
ushort width, height;
|
||||||
|
Font font;
|
||||||
|
SpriteAtlas sprite_atlas;
|
||||||
|
|
||||||
|
bool has_focus;
|
||||||
|
InputData input, current_input;
|
||||||
|
|
||||||
Id hover_id;
|
Id hover_id;
|
||||||
Id focus_id;
|
Id focus_id;
|
||||||
|
|
||||||
@ -175,6 +177,7 @@ fn PElemTuple? Ctx.get_elem(&ctx, Id id, ElemType type)
|
|||||||
elem = ctx.cache.get_or_insert(&&(Elem){}, id, &is_new)!;
|
elem = ctx.cache.get_or_insert(&&(Elem){}, id, &is_new)!;
|
||||||
elem.flags = (ElemFlags)0;
|
elem.flags = (ElemFlags)0;
|
||||||
elem.flags.is_new = is_new;
|
elem.flags.is_new = is_new;
|
||||||
|
elem.flags.shown = true;
|
||||||
elem.id = id;
|
elem.id = id;
|
||||||
elem.layout = {};
|
elem.layout = {};
|
||||||
if (is_new == false && elem.type != type) {
|
if (is_new == false && elem.type != type) {
|
||||||
@ -247,7 +250,6 @@ fn void? Ctx.frame_begin(&ctx)
|
|||||||
// if the window has focus then the root element also has focus, no other
|
// if the window has focus then the root element also has focus, no other
|
||||||
// computation needed, child elements need to check the mouse positon and
|
// computation needed, child elements need to check the mouse positon and
|
||||||
// other stuff
|
// other stuff
|
||||||
ctx.input.z_index = 0;
|
|
||||||
//elem.flags.has_focus = ctx.has_focus;
|
//elem.flags.has_focus = ctx.has_focus;
|
||||||
|
|
||||||
elem.bounds = {0, 0, ctx.width, ctx.height};
|
elem.bounds = {0, 0, ctx.width, ctx.height};
|
||||||
@ -260,30 +262,41 @@ fn void? Ctx.frame_begin(&ctx)
|
|||||||
elem.layout.h = @exact(ctx.height);
|
elem.layout.h = @exact(ctx.height);
|
||||||
|
|
||||||
ctx.div_scissor = elem.bounds;
|
ctx.div_scissor = elem.bounds;
|
||||||
|
|
||||||
// The root element does not push anything to the stack
|
|
||||||
// TODO: add a background color taken from a theme or config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.frame_end(&ctx)
|
fn void? Ctx.frame_end(&ctx)
|
||||||
{
|
{
|
||||||
// FIXME: this is not guaranteed to be root. the user might forget to close a div or some other element
|
|
||||||
Elem* root = ctx.get_active_div()!;
|
Elem* root = ctx.get_active_div()!;
|
||||||
if (root.id != ROOT_ID) {
|
if (root.id != ROOT_ID) {
|
||||||
io::printn(root.id);
|
|
||||||
return WRONG_ID?;
|
return WRONG_ID?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. clear input fields
|
||||||
|
ctx.input = ctx.current_input;
|
||||||
|
ctx.current_input.events = {};
|
||||||
|
ctx.current_input.keyboard.text_len = 0;
|
||||||
|
|
||||||
// DO THE LAYOUT
|
// DO THE LAYOUT
|
||||||
ctx.layout_element_tree()!;
|
ctx.layout_element_tree()!;
|
||||||
|
|
||||||
|
foreach (idx, id : ctx.tree.elem_vec) {
|
||||||
|
if (!ctx.tree.is_used((int)idx)) continue;
|
||||||
|
Elem* c = ctx.find_elem(id);
|
||||||
|
// reset events
|
||||||
|
c.events = {};
|
||||||
|
// reset shown flag
|
||||||
|
// TODO: use shown_last_frame to avoid this loop entirely
|
||||||
|
c.flags.shown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate input events to the right elements
|
||||||
|
ctx.set_elem_events(ctx.hover_id);
|
||||||
|
ctx.set_elem_events(ctx.focus_id);
|
||||||
|
|
||||||
|
|
||||||
// 1. clear the tree
|
// 1. clear the tree
|
||||||
ctx.tree.nuke();
|
ctx.tree.nuke();
|
||||||
|
|
||||||
// 2. clear input fields
|
|
||||||
ctx.input.events = (InputEvents)0;
|
|
||||||
ctx.input.keyboard.text_len = 0;
|
|
||||||
|
|
||||||
// send atlas updates
|
// send atlas updates
|
||||||
if (ctx.font.should_update) {
|
if (ctx.font.should_update) {
|
||||||
ctx.push_update_atlas(&ctx.font.atlas)!;
|
ctx.push_update_atlas(&ctx.font.atlas)!;
|
||||||
@ -323,38 +336,47 @@ $endif
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
|
||||||
* @ensure elem != null
|
|
||||||
*>
|
|
||||||
macro bool Ctx.is_hovered(&ctx, Elem *elem)
|
|
||||||
{
|
|
||||||
return ctx.input.mouse.pos.in_rect(elem.bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro bool Ctx.elem_focus(&ctx, Elem *elem)
|
macro bool Ctx.is_hovered(&ctx, Elem *elem) => ctx.input.mouse.pos.in_rect(elem.bounds);
|
||||||
{
|
|
||||||
return ctx.focus_id == elem.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add other events
|
// Check if the element is hovered and/or focused, if it is update the context ids.
|
||||||
// FIXME: this does not work with touch
|
// The order in which the elements are passed to this function is not relevant
|
||||||
// FIXME: hacked together, please do better
|
fn void Ctx.update_hover_and_focus(&ctx, Elem* elem)
|
||||||
fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem)
|
|
||||||
{
|
{
|
||||||
if (ctx.input.z_index != elem.z_index) return {};
|
|
||||||
|
|
||||||
bool hover = ctx.is_hovered(elem);
|
bool hover = ctx.is_hovered(elem);
|
||||||
bool focus = ctx.elem_focus(elem) || (hover && ctx.is_mouse_pressed(BTN_LEFT));
|
bool focus = ctx.focus_id == elem.id || (hover && ctx.is_mouse_pressed(BTN_ANY));
|
||||||
|
|
||||||
if (ctx.is_mouse_pressed(BTN_ANY) && !hover){
|
if (hover) {
|
||||||
focus = false;
|
Elem* prev_hover = ctx.find_elem(ctx.hover_id);
|
||||||
if (ctx.focus_id == elem.id) ctx.focus_id = 0;
|
bool different = prev_hover.id != elem.id;
|
||||||
|
bool still_hovered = ctx.is_hovered(prev_hover);
|
||||||
|
bool shown = prev_hover.flags.shown;
|
||||||
|
bool above = prev_hover.z_index > elem.z_index;
|
||||||
|
|
||||||
|
hover = !(different && still_hovered && shown && above);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hover) { ctx.hover_id = elem.id; }
|
if (focus) {
|
||||||
if (focus) { ctx.focus_id = elem.id; }
|
Elem* prev_focus = ctx.find_elem(ctx.hover_id);
|
||||||
|
bool different = prev_focus.id != elem.id;
|
||||||
|
bool shown = prev_focus.flags.shown;
|
||||||
|
bool above = prev_focus.z_index > elem.z_index;
|
||||||
|
|
||||||
ElemEvents ev = {
|
focus = !(different && shown && above);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hover) ctx.hover_id = elem.id;
|
||||||
|
if (focus) ctx.focus_id = elem.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this does not work with touch
|
||||||
|
fn void Ctx.set_elem_events(&ctx, Id id)
|
||||||
|
{
|
||||||
|
bool hover = id == ctx.hover_id;
|
||||||
|
bool focus = id == ctx.focus_id;
|
||||||
|
Elem* e = ctx.find_elem(id);
|
||||||
|
|
||||||
|
e.events = {
|
||||||
.has_focus = focus,
|
.has_focus = focus,
|
||||||
.mouse_hover = hover,
|
.mouse_hover = hover,
|
||||||
.mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY),
|
.mouse_press = hover && focus && ctx.is_mouse_pressed(BTN_ANY),
|
||||||
@ -365,5 +387,4 @@ fn ElemEvents Ctx.get_elem_events(&ctx, Elem *elem)
|
|||||||
.key_repeat = focus && ctx.input.events.key_repeat,
|
.key_repeat = focus && ctx.input.events.key_repeat,
|
||||||
.text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT),
|
.text_input = focus && (ctx.input.keyboard.text_len || ctx.input.keyboard.modkeys & KMOD_TXT),
|
||||||
};
|
};
|
||||||
return ev;
|
|
||||||
}
|
}
|
||||||
|
@ -71,7 +71,7 @@ const ModKeys KEY_ANY = (ModKeys)(ModKeys.inner.max);
|
|||||||
|
|
||||||
fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String ...keys)
|
fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String ...keys)
|
||||||
{
|
{
|
||||||
bool is_mod = (bool)(ctx.input.keyboard.modkeys & mod);
|
bool is_mod = (bool)(ctx.current_input.keyboard.modkeys & mod);
|
||||||
bool is_keys = true;
|
bool is_keys = true;
|
||||||
String haystack = (String)ctx.get_keys();
|
String haystack = (String)ctx.get_keys();
|
||||||
foreach (needle: keys) {
|
foreach (needle: keys) {
|
||||||
@ -86,7 +86,7 @@ fn void? Ctx.input_window_size(&ctx, short width, short height)
|
|||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
return INVALID_SIZE?;
|
return INVALID_SIZE?;
|
||||||
}
|
}
|
||||||
ctx.input.events.resize = ctx.width != width || ctx.height != height;
|
ctx.current_input.events.resize = ctx.width != width || ctx.height != height;
|
||||||
ctx.width = width;
|
ctx.width = width;
|
||||||
ctx.height = height;
|
ctx.height = height;
|
||||||
}
|
}
|
||||||
@ -96,13 +96,13 @@ fn void Ctx.input_changefocus(&ctx, bool has_focus)
|
|||||||
{
|
{
|
||||||
// FIXME: raylib only has an API to query the focus status so we have to
|
// FIXME: raylib only has an API to query the focus status so we have to
|
||||||
// update the input flag only if the focus changed
|
// update the input flag only if the focus changed
|
||||||
ctx.input.events.change_focus = ctx.has_focus != has_focus;
|
ctx.current_input.events.change_focus = ctx.has_focus != has_focus;
|
||||||
ctx.has_focus = has_focus;
|
ctx.has_focus = has_focus;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Ctx.mouse_pressed(&ctx) => ctx.input.mouse.updated & ctx.input.mouse.down;
|
macro Ctx.mouse_pressed(&ctx) => ctx.current_input.mouse.updated & ctx.current_input.mouse.down;
|
||||||
macro Ctx.mouse_released(&ctx) => ctx.input.mouse.updated & ~ctx.input.mouse.down;
|
macro Ctx.mouse_released(&ctx) => ctx.current_input.mouse.updated & ~ctx.current_input.mouse.down;
|
||||||
macro Ctx.mouse_down(&ctx) => ctx.input.mouse.down;
|
macro Ctx.mouse_down(&ctx) => ctx.current_input.mouse.down;
|
||||||
|
|
||||||
// FIXME: hthis compairson could be done with a cast using MouseButtons.inner
|
// FIXME: hthis compairson could be done with a cast using MouseButtons.inner
|
||||||
// property but I could not figure out how
|
// property but I could not figure out how
|
||||||
@ -113,63 +113,63 @@ macro Ctx.is_mouse_down(&ctx, MouseButtons btn) => (ctx.mouse_down() & btn) != B
|
|||||||
// Mouse Buttons down
|
// Mouse Buttons down
|
||||||
fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons)
|
fn void Ctx.input_mouse_button(&ctx, MouseButtons buttons)
|
||||||
{
|
{
|
||||||
ctx.input.mouse.updated = ctx.input.mouse.down ^ buttons;
|
ctx.current_input.mouse.updated = ctx.current_input.mouse.down ^ buttons;
|
||||||
ctx.input.mouse.down = buttons;
|
ctx.current_input.mouse.down = buttons;
|
||||||
ctx.input.events.mouse_btn = (uint)ctx.input.mouse.down != 0 || (uint)ctx.input.mouse.updated != 0;
|
ctx.current_input.events.mouse_btn = (uint)ctx.current_input.mouse.down != 0 || (uint)ctx.current_input.mouse.updated != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse was moved, report absolute position
|
// Mouse was moved, report absolute position
|
||||||
fn void Ctx.input_mouse_abs(&ctx, short x, short y)
|
fn void Ctx.input_mouse_abs(&ctx, short x, short y)
|
||||||
{
|
{
|
||||||
ctx.input.mouse.pos.x = math::clamp(x, (short)0, ctx.width);
|
ctx.current_input.mouse.pos.x = math::clamp(x, (short)0, ctx.width);
|
||||||
ctx.input.mouse.pos.y = math::clamp(y, (short)0, ctx.height);
|
ctx.current_input.mouse.pos.y = math::clamp(y, (short)0, ctx.height);
|
||||||
|
|
||||||
short dx, dy;
|
short dx, dy;
|
||||||
dx = x - ctx.input.mouse.pos.x;
|
dx = x - ctx.current_input.mouse.pos.x;
|
||||||
dy = y - ctx.input.mouse.pos.y;
|
dy = y - ctx.current_input.mouse.pos.y;
|
||||||
|
|
||||||
ctx.input.mouse.delta.x = dx;
|
ctx.current_input.mouse.delta.x = dx;
|
||||||
ctx.input.mouse.delta.y = dy;
|
ctx.current_input.mouse.delta.y = dy;
|
||||||
|
|
||||||
ctx.input.events.mouse_move = dx != 0 || dy != 0;
|
ctx.current_input.events.mouse_move = dx != 0 || dy != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mouse was moved, report relative motion
|
// Mouse was moved, report relative motion
|
||||||
fn void Ctx.input_mouse_delta(&ctx, short dx, short dy)
|
fn void Ctx.input_mouse_delta(&ctx, short dx, short dy)
|
||||||
{
|
{
|
||||||
ctx.input.mouse.delta.x = dx;
|
ctx.current_input.mouse.delta.x = dx;
|
||||||
ctx.input.mouse.delta.y = dy;
|
ctx.current_input.mouse.delta.y = dy;
|
||||||
|
|
||||||
short mx, my;
|
short mx, my;
|
||||||
mx = ctx.input.mouse.pos.x + dx;
|
mx = ctx.current_input.mouse.pos.x + dx;
|
||||||
my = ctx.input.mouse.pos.y + dy;
|
my = ctx.current_input.mouse.pos.y + dy;
|
||||||
|
|
||||||
ctx.input.mouse.pos.x = math::clamp(mx, (short)0, ctx.width);
|
ctx.current_input.mouse.pos.x = math::clamp(mx, (short)0, ctx.width);
|
||||||
ctx.input.mouse.pos.y = math::clamp(my, (short)0, ctx.height);
|
ctx.current_input.mouse.pos.y = math::clamp(my, (short)0, ctx.height);
|
||||||
|
|
||||||
ctx.input.events.mouse_move = dx != 0 || dy != 0;
|
ctx.current_input.events.mouse_move = dx != 0 || dy != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_mouse_wheel(&ctx, short x, short y, float scale = 1.0)
|
fn void Ctx.input_mouse_wheel(&ctx, short x, short y, float scale = 1.0)
|
||||||
{
|
{
|
||||||
ctx.input.mouse.scroll.x = (short)((float)-x*scale);
|
ctx.current_input.mouse.scroll.x = (short)((float)-x*scale);
|
||||||
ctx.input.mouse.scroll.y = (short)((float)-y*scale);
|
ctx.current_input.mouse.scroll.y = (short)((float)-y*scale);
|
||||||
ctx.input.events.mouse_scroll = x !=0 || y != 0;
|
ctx.current_input.events.mouse_scroll = x !=0 || y != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_key_press(&ctx)
|
fn void Ctx.input_key_press(&ctx)
|
||||||
{
|
{
|
||||||
ctx.input.events.key_press = true;
|
ctx.current_input.events.key_press = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_key_release(&ctx)
|
fn void Ctx.input_key_release(&ctx)
|
||||||
{
|
{
|
||||||
ctx.input.events.key_release = true;
|
ctx.current_input.events.key_release = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_key_repeat(&ctx)
|
fn void Ctx.input_key_repeat(&ctx)
|
||||||
{
|
{
|
||||||
ctx.input.events.key_repeat = true;
|
ctx.current_input.events.key_repeat = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// append utf-8 encoded text to the context text input
|
// append utf-8 encoded text to the context text input
|
||||||
@ -177,12 +177,12 @@ fn void Ctx.input_text_utf8(&ctx, char[] text)
|
|||||||
{
|
{
|
||||||
if (text.len == 0) { return; }
|
if (text.len == 0) { return; }
|
||||||
|
|
||||||
usz remaining = ctx.input.keyboard.text.len - ctx.input.keyboard.text_len;
|
usz remaining = ctx.current_input.keyboard.text.len - ctx.current_input.keyboard.text_len;
|
||||||
usz len = text.len > remaining ? remaining : text.len;
|
usz len = text.len > remaining ? remaining : text.len;
|
||||||
char[] s = ctx.input.keyboard.text[ctx.input.keyboard.text_len ..];
|
char[] s = ctx.current_input.keyboard.text[ctx.current_input.keyboard.text_len ..];
|
||||||
s[..len-1] = text[..len-1];
|
s[..len-1] = text[..len-1];
|
||||||
ctx.input.keyboard.text_len += len;
|
ctx.current_input.keyboard.text_len += len;
|
||||||
ctx.input.events.text_input = true;
|
ctx.current_input.events.text_input = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_text_unicode(&ctx, char[] text)
|
fn void Ctx.input_text_unicode(&ctx, char[] text)
|
||||||
@ -190,8 +190,8 @@ fn void Ctx.input_text_unicode(&ctx, char[] text)
|
|||||||
if (text.ptr == null || text.len == 0) { return; }
|
if (text.ptr == null || text.len == 0) { return; }
|
||||||
|
|
||||||
char[32] tmp;
|
char[32] tmp;
|
||||||
usz remaining = ctx.input.keyboard.text.len - ctx.input.keyboard.text_len;
|
usz remaining = ctx.current_input.keyboard.text.len - ctx.current_input.keyboard.text_len;
|
||||||
char[] s = ctx.input.keyboard.text[ctx.input.keyboard.text_len ..];
|
char[] s = ctx.current_input.keyboard.text[ctx.current_input.keyboard.text_len ..];
|
||||||
|
|
||||||
usz off;
|
usz off;
|
||||||
foreach (idx, cp: text) {
|
foreach (idx, cp: text) {
|
||||||
@ -200,9 +200,9 @@ fn void Ctx.input_text_unicode(&ctx, char[] text)
|
|||||||
s[off..off+enc] = tmp[..enc];
|
s[off..off+enc] = tmp[..enc];
|
||||||
off += enc;
|
off += enc;
|
||||||
}
|
}
|
||||||
ctx.input.keyboard.text_len += off;
|
ctx.current_input.keyboard.text_len += off;
|
||||||
|
|
||||||
ctx.input.events.text_input = true;
|
ctx.current_input.events.text_input = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void Ctx.input_char(&ctx, char c)
|
fn void Ctx.input_char(&ctx, char c)
|
||||||
@ -218,6 +218,6 @@ fn ModKeys Ctx.get_mod(&ctx) => ctx.input.keyboard.modkeys;
|
|||||||
// TODO: make this call repetible to input modkeys one by one
|
// TODO: make this call repetible to input modkeys one by one
|
||||||
fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys)
|
fn void Ctx.input_mod_keys(&ctx, ModKeys modkeys)
|
||||||
{
|
{
|
||||||
ctx.input.keyboard.modkeys = modkeys;
|
ctx.current_input.keyboard.modkeys = modkeys;
|
||||||
ctx.input.events.mod_key = (uint)ctx.input.keyboard.modkeys != 0;
|
ctx.current_input.events.mod_key = (uint)ctx.current_input.keyboard.modkeys != 0;
|
||||||
}
|
}
|
||||||
|
@ -335,6 +335,10 @@ fn void? Ctx.layout_element_tree(&ctx)
|
|||||||
resolve_placement(c, p);
|
resolve_placement(c, p);
|
||||||
update_children_bounds(c, p);
|
update_children_bounds(c, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: this stuff would be better elsewhere but we are already iteraring through all
|
||||||
|
// elements so here it fits really well
|
||||||
|
ctx.update_hover_and_focus(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,6 +199,8 @@ macro bool Point.in_rect(Point p, Rect r)
|
|||||||
return (p.x >= r.x && p.x <= r.x + r.w) && (p.y >= r.y && p.y <= r.y + r.h);
|
return (p.x >= r.x && p.x <= r.x + r.w) && (p.y >= r.y && p.y <= r.y + r.h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro bool Point.outside(Point p, Rect r) => !p.in_rect(r);
|
||||||
|
|
||||||
macro Point Point.add(Point a, Point b) @operator_s(+) => {.x = a.x+b.x, .y = a.y+b.y};
|
macro Point Point.add(Point a, Point b) @operator_s(+) => {.x = a.x+b.x, .y = a.y+b.y};
|
||||||
macro Point Point.sub(Point a, Point b) @operator_s(-) => {.x = a.x-b.x, .y = a.y-b.y};
|
macro Point Point.sub(Point a, Point b) @operator_s(-) => {.x = a.x-b.x, .y = a.y-b.y};
|
||||||
macro Point Point.neg(Point p) @operator_s(-) => {-p.x, -p.y};
|
macro Point Point.neg(Point p) @operator_s(-) => {-p.x, -p.y};
|
||||||
@ -241,20 +243,22 @@ macro uint Color.to_uint(c) => c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
|||||||
// SIZE //
|
// SIZE //
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
macro short short.add_no_of(short a, short b) => (short)max(min((int)a + (int)b, short.max), short.min) @inline;
|
||||||
|
|
||||||
struct Size {
|
struct Size {
|
||||||
short min, max;
|
short min, max;
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Size @grow() => {.min = 0, .max = 0};
|
macro Size @grow() => {.min = 0, .max = 0};
|
||||||
macro Size @exact(short s) => {.min = s, .max = s};
|
macro Size @exact(short s) => {.min = s, .max = s};
|
||||||
// PROBLEM WE ARE OVERFLOWING
|
macro Size @fit(short min = 0, short max = short.max) => {.min = min, .max = max};
|
||||||
macro Size @fit(short min = 0, short max = 999) => {.min = min, .max = max};
|
|
||||||
macro bool Size.@is_grow(s) => (s.min == 0 && s.max == 0);
|
macro bool Size.@is_grow(s) => (s.min == 0 && s.max == 0);
|
||||||
macro bool Size.@is_exact(s) => (s.min == s.max && s.min != 0);
|
macro bool Size.@is_exact(s) => (s.min == s.max && s.min != 0);
|
||||||
macro bool Size.@is_fit(s) => (s.min != s.max);
|
macro bool Size.@is_fit(s) => (s.min != s.max);
|
||||||
|
|
||||||
macro Size Size.add(a, Size b) @operator_s(+) => {.min = a.min+b.min, .max = a.max+b.max};
|
macro Size Size.add(a, Size b) @operator_s(+) => {.min = a.min.add_no_of(b.min), .max = a.max.add_no_of(b.max)};
|
||||||
macro Size Size.sub(a, Size b) @operator_s(-) => {.min = a.min-b.min, .max = a.max-b.max};
|
macro Size Size.sub(a, Size b) @operator_s(-) => {.min = a.min.add_no_of(-b.min), .max = a.max.add_no_of(-b.max)};
|
||||||
|
|
||||||
macro Size Size.combine(a, Size b) => {.min = max(a.min, b.min), .max = min(a.max, b.max)};
|
macro Size Size.combine(a, Size b) => {.min = max(a.min, b.min), .max = min(a.max, b.max)};
|
||||||
macro Size Size.comb_max(a, Size b) => {.min = max(a.min, b.min), .max = max(a.max, b.max)};
|
macro Size Size.comb_max(a, Size b) => {.min = max(a.min, b.min), .max = max(a.max, b.max)};
|
||||||
|
@ -46,7 +46,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
|
|
||||||
update_parent_size(elem, parent);
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
// if (ctx.input.z_index == elem.z_index) println("true ", elem.z_index);
|
// if (ctx.input.z_index == elem.z_index) println("true ", elem.z_index);
|
||||||
Rect content_bounds = elem.content_bounds();
|
Rect content_bounds = elem.content_bounds();
|
||||||
|
|
||||||
@ -66,7 +66,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
};
|
};
|
||||||
//text_bounds = text_size.center_to(text_bounds);
|
//text_bounds = text_size.center_to(text_bounds);
|
||||||
|
|
||||||
bool is_active = ctx.elem_focus(elem) || elem.events.mouse_hover;
|
bool is_active = elem.events.has_focus || elem.events.mouse_hover;
|
||||||
Style s = *style;
|
Style s = *style;
|
||||||
if (is_active) {
|
if (is_active) {
|
||||||
s.secondary = s.primary;
|
s.secondary = s.primary;
|
||||||
@ -115,7 +115,7 @@ fn void? Ctx.checkbox_id(&ctx, Id id, String description, bool* active, String t
|
|||||||
|
|
||||||
update_parent_size(elem, parent);
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
|
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
|
||||||
|
|
||||||
|
|
||||||
@ -189,7 +189,7 @@ fn void? Ctx.toggle_id(&ctx, Id id, String description, bool* active)
|
|||||||
|
|
||||||
update_parent_size(elem, parent);
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
|
if (elem.events.mouse_hover && elem.events.mouse_release) *active = !(*active);
|
||||||
|
|
||||||
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
|
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
|
||||||
|
@ -18,6 +18,13 @@ struct ElemDiv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
macro Ctx.@center(&ctx, LayoutDirection dir = ROW, ...; @body())
|
||||||
|
{
|
||||||
|
ctx.@div(@grow(), @grow(), dir, CENTER) {
|
||||||
|
@body();
|
||||||
|
}!;
|
||||||
|
}
|
||||||
|
|
||||||
macro Ctx.@row(&ctx, Anchor anchor = TOP_LEFT, ...; @body())
|
macro Ctx.@row(&ctx, Anchor anchor = TOP_LEFT, ...; @body())
|
||||||
{
|
{
|
||||||
ctx.@div(@fit(), @fit(), ROW, anchor: anchor) {
|
ctx.@div(@fit(), @fit(), ROW, anchor: anchor) {
|
||||||
@ -80,7 +87,6 @@ fn void? Ctx.div_begin_id(&ctx,
|
|||||||
|
|
||||||
elem.div.scroll_x.enabled = scroll_x;
|
elem.div.scroll_x.enabled = scroll_x;
|
||||||
elem.div.scroll_y.enabled = scroll_y;
|
elem.div.scroll_y.enabled = scroll_y;
|
||||||
if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index;
|
|
||||||
|
|
||||||
// update layout with correct info
|
// update layout with correct info
|
||||||
elem.layout = {
|
elem.layout = {
|
||||||
@ -102,7 +108,7 @@ fn void? Ctx.div_begin_id(&ctx,
|
|||||||
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
|
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
|
||||||
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
|
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
// TODO: check active
|
// TODO: check active
|
||||||
// TODO: check resizeable
|
// TODO: check resizeable
|
||||||
@ -192,7 +198,6 @@ fn bool? Ctx.popup_begin_id(&ctx,
|
|||||||
elem.div.scroll_x.enabled = scroll_x;
|
elem.div.scroll_x.enabled = scroll_x;
|
||||||
elem.div.scroll_y.enabled = scroll_y;
|
elem.div.scroll_y.enabled = scroll_y;
|
||||||
elem.z_index++;
|
elem.z_index++;
|
||||||
if (ctx.input.z_index < elem.z_index) ctx.input.z_index = elem.z_index;
|
|
||||||
|
|
||||||
// update layout with correct info
|
// update layout with correct info
|
||||||
elem.layout = {
|
elem.layout = {
|
||||||
@ -212,10 +217,10 @@ fn bool? Ctx.popup_begin_id(&ctx,
|
|||||||
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
|
ctx.div_scissor = elem.bounds.pad(elem.layout.content_offset);
|
||||||
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
|
ctx.push_scissor(elem.bounds.pad(elem.layout.content_offset), elem.z_index)!;
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
// check close condition, mouse release anywhere outside the div bounds
|
// check close condition, mouse release anywhere outside the div bounds
|
||||||
if ((ctx.mouse_released() & BTN_ANY) && !elem.events.mouse_hover) {
|
if ((ctx.mouse_released() & BTN_ANY) && ctx.input.mouse.pos.outside(elem.bounds)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,9 +45,9 @@ fn ElemEvents? Ctx.slider_hor_id(&ctx, Id id, Size w, Size h, float* value, floa
|
|||||||
elem.slider.handle = handle;
|
elem.slider.handle = handle;
|
||||||
|
|
||||||
Point m = ctx.input.mouse.pos;
|
Point m = ctx.input.mouse.pos;
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
if (ctx.elem_focus(elem) && ctx.is_mouse_down(BTN_LEFT)) {
|
if (elem.events.has_focus && ctx.is_mouse_down(BTN_LEFT)) {
|
||||||
*value = calc_value(content_bounds.x, m.x, content_bounds.w, hw);
|
*value = calc_value(content_bounds.x, m.x, content_bounds.w, hw);
|
||||||
elem.slider.handle.x = calc_slider(content_bounds.x, content_bounds.w-hw, *value);
|
elem.slider.handle.x = calc_slider(content_bounds.x, content_bounds.w-hw, *value);
|
||||||
elem.events.update = true;
|
elem.events.update = true;
|
||||||
@ -109,9 +109,9 @@ fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Size w, Size h, float* value, floa
|
|||||||
elem.slider.handle = handle;
|
elem.slider.handle = handle;
|
||||||
|
|
||||||
Point m = ctx.input.mouse.pos;
|
Point m = ctx.input.mouse.pos;
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
if (ctx.elem_focus(elem) && ctx.is_mouse_down(BTN_LEFT)) {
|
if (elem.events.has_focus && ctx.is_mouse_down(BTN_LEFT)) {
|
||||||
*value = calc_value(content_bounds.y, m.y, content_bounds.h, hh);
|
*value = calc_value(content_bounds.y, m.y, content_bounds.h, hh);
|
||||||
elem.slider.handle.y = calc_slider(content_bounds.y, content_bounds.h-hh, *value);
|
elem.slider.handle.y = calc_slider(content_bounds.y, content_bounds.h-hh, *value);
|
||||||
elem.events.update = true;
|
elem.events.update = true;
|
||||||
@ -156,7 +156,7 @@ fn void? Ctx.scrollbar(&ctx, Id id, float *value, float handle_percent, bool ver
|
|||||||
update_parent_size(elem, parent);
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
|
Rect content_bounds = elem.bounds.pad(elem.layout.content_offset);
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
short o = vertical ? content_bounds.y : content_bounds.x;
|
short o = vertical ? content_bounds.y : content_bounds.x;
|
||||||
short m = vertical ? ctx.input.mouse.pos.y : ctx.input.mouse.pos.x;
|
short m = vertical ? ctx.input.mouse.pos.y : ctx.input.mouse.pos.x;
|
||||||
|
@ -65,7 +65,7 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Size w, Size h, TextEdit* te, Anchor
|
|||||||
update_parent_size(elem, parent);
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
// check input and update the text
|
// check input and update the text
|
||||||
elem.events = ctx.get_elem_events(elem);
|
//elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
if (elem.events.text_input || elem.events.key_press) {
|
if (elem.events.text_input || elem.events.key_press) {
|
||||||
ctx.text_edit(elem.text.te);
|
ctx.text_edit(elem.text.te);
|
||||||
|
24
src/main.c3
24
src/main.c3
@ -214,6 +214,8 @@ $case "debug":
|
|||||||
debug_app(&ui);
|
debug_app(&ui);
|
||||||
$case "calculator":
|
$case "calculator":
|
||||||
calculator(&ui, &te);
|
calculator(&ui, &te);
|
||||||
|
$case "popup":
|
||||||
|
popup(&ui);
|
||||||
$endswitch
|
$endswitch
|
||||||
|
|
||||||
// Timings counter
|
// Timings counter
|
||||||
@ -395,6 +397,7 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
|
|||||||
ui.button("Quattro")!!;
|
ui.button("Quattro")!!;
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ui input/output
|
// ui input/output
|
||||||
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen
|
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) { // center everything on the screen
|
||||||
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) {
|
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN, TOP_LEFT) {
|
||||||
@ -459,5 +462,24 @@ fn void calculator(ugui::Ctx* ui, TextEdit* te)
|
|||||||
ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!;
|
ui.text_box(ugui::@grow(), ugui::@exact(100), te, RIGHT)!!;
|
||||||
}!!;
|
}!!;
|
||||||
}!!; }!!;
|
}!!; }!!;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void popup(ugui::Ctx* ui)
|
||||||
|
{
|
||||||
|
static bool toggle;
|
||||||
|
static ugui::Point pos;
|
||||||
|
|
||||||
|
if (toggle) {
|
||||||
|
toggle = ui.popup_begin(pos, ugui::@fit(), ugui::@fit(), COLUMN)!!;
|
||||||
|
ui.button("POP")!!;
|
||||||
|
ui.button("UP")!!;
|
||||||
|
ui.div_end()!!;
|
||||||
|
}
|
||||||
|
ui.@center(COLUMN) {
|
||||||
|
if (ui.button("ciao")!!.mouse_release) {
|
||||||
|
pos = ui.input.mouse.pos;
|
||||||
|
toggle = ~toggle;
|
||||||
|
}
|
||||||
|
if (ui.button("mamma")!!.mouse_press) ugui::println("pressed!");
|
||||||
|
}!!;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user