unified button element
This commit is contained in:
parent
b48c413d2d
commit
80d17d7b33
14
TODO
14
TODO
@ -15,8 +15,10 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[x] Implement a z index and sort command buffer based on that
|
[x] Implement a z index and sort command buffer based on that
|
||||||
[ ] Ctx.set_z_index()
|
[ ] Ctx.set_z_index()
|
||||||
[x] Sort command buffer on insertion
|
[x] Sort command buffer on insertion
|
||||||
[x] Standardize element handling, for example all buttons do almost the same thing, so write a lot of boiler plate and reuse it
|
[x] Standardize element handling, for example all buttons do almost the same thing, so write a lot
|
||||||
[x] The id combination in gen_id() uses an intger division, which is costly, use another combination function that is non-linear and doesn't use division
|
of boiler plate and reuse it
|
||||||
|
[x] The id combination in gen_id() uses an intger division, which is costly, use another combination
|
||||||
|
function that is non-linear and doesn't use division
|
||||||
[ ] Animations, somehow
|
[ ] Animations, somehow
|
||||||
[ ] Maybe cache codepoint converted strings
|
[ ] Maybe cache codepoint converted strings
|
||||||
[x] Fix scroll wheel when div is scrolled
|
[x] Fix scroll wheel when div is scrolled
|
||||||
@ -39,10 +41,10 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
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
|
||||||
[ ] Border and padding do not go well together if the library issues two rect commands, the visible
|
[x] Fix how padding is applied in push_rect. In CSS padding is applied between the border and the
|
||||||
border is effectively the border size plus the padding since there is a gap between the border
|
content, the background color is applied starting from the border. Right now push_rect() offsets
|
||||||
rect and the internal rect. A better solution is to leave it up to the renderer to draw the rect
|
the background rect by both border and padding
|
||||||
correctly
|
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
|
@ -8,110 +8,80 @@ struct ElemButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// draw a button, return the events on that button
|
macro Ctx.button(&ctx, String label = "", String icon = "", ...)
|
||||||
macro Ctx.button(&ctx, Rect size, bool state = false, ...)
|
=> ctx.button_id(@compute_id($vasplat), label, icon);
|
||||||
=> ctx.button_id(@compute_id($vasplat), size, state);
|
fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
||||||
fn ElemEvents? Ctx.button_id(&ctx, Id id, Rect size, bool active)
|
|
||||||
{
|
{
|
||||||
id = ctx.gen_id(id)!;
|
id = ctx.gen_id(id)!;
|
||||||
|
|
||||||
Elem *parent = ctx.get_parent()!;
|
Elem *parent = ctx.get_parent()!;
|
||||||
Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!;
|
Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!;
|
||||||
Style* style_norm = ctx.styles.get_style(@str_hash("button"));
|
Style* style = ctx.styles.get_style(@str_hash("button"));
|
||||||
|
|
||||||
elem.bounds = ctx.position_element(parent, size, style_norm);
|
|
||||||
|
|
||||||
// if the bounds are null the element is outside the div view,
|
|
||||||
// no interaction should occur so just return
|
|
||||||
if (elem.bounds.is_null()) { return {}; }
|
|
||||||
|
|
||||||
Style* style_active = ctx.styles.get_style(@str_hash("button-active"));
|
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
|
||||||
bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover;
|
|
||||||
|
|
||||||
// Draw the button
|
|
||||||
ctx.push_rect(elem.bounds, parent.div.z_index, is_active ? style_active : style_norm)!;
|
|
||||||
|
|
||||||
return elem.events;
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool active = false, ...)
|
|
||||||
=> ctx.button_label_id(@compute_id($vasplat), label, size, active);
|
|
||||||
fn ElemEvents? Ctx.button_label_id(&ctx, Id id, String label, Rect size, bool active)
|
|
||||||
{
|
|
||||||
id = ctx.gen_id(id)!;
|
|
||||||
|
|
||||||
Elem *parent = ctx.get_parent()!;
|
|
||||||
Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!;
|
|
||||||
Style* style_norm = ctx.styles.get_style(@str_hash("button"));
|
|
||||||
Style* style_active = ctx.styles.get_style(@str_hash("button-active"));
|
|
||||||
|
|
||||||
// TODO: cache text_size just like text_unbounded() does
|
|
||||||
Rect text_size = ctx.get_text_bounds(label)!;
|
|
||||||
Rect btn_size = text_size.grow({10,10});
|
|
||||||
|
|
||||||
// 2. Layout
|
|
||||||
elem.bounds = ctx.position_element(parent, btn_size, style_norm);
|
|
||||||
if (elem.bounds.is_null()) { return {}; }
|
|
||||||
|
|
||||||
text_size.x = elem.bounds.x;
|
Sprite* sprite = icon != "" ? ctx.sprite_atlas.get(icon)! : &&(Sprite){};
|
||||||
text_size.y = elem.bounds.y;
|
|
||||||
text_size = text_size.center_to(elem.bounds);
|
|
||||||
|
|
||||||
|
Rect min_size = {0, 0, style.size, style.size};
|
||||||
|
Rect text_size = ctx.get_text_bounds(label)!;
|
||||||
|
Rect icon_size = sprite.rect();
|
||||||
|
|
||||||
|
ushort h_lh = (ushort)(ctx.font.line_height() / 2);
|
||||||
|
ushort left_pad = label != "" ? h_lh : 0;
|
||||||
|
ushort inner_pad = label != "" && icon != "" ? h_lh : 0;
|
||||||
|
ushort right_pad = left_pad;
|
||||||
|
|
||||||
|
/* |left_pad
|
||||||
|
* +--v-----------------------------------+
|
||||||
|
* |<->+--------+ |
|
||||||
|
* | | | +-----------------+ |
|
||||||
|
* | | icon | | label | |
|
||||||
|
* | | | +-----------------+ |
|
||||||
|
* | +--------+<->| |<->|
|
||||||
|
* +-------------^--------------------^---+
|
||||||
|
* |inner_pad |right_pad
|
||||||
|
*/
|
||||||
|
|
||||||
|
Rect tot_size = {
|
||||||
|
.w = (short)max(min_size.w, icon_size.w + text_size.w + left_pad + inner_pad + right_pad),
|
||||||
|
.h = (short)max(min_size.h, max(icon_size.h, text_size.h)),
|
||||||
|
};
|
||||||
|
|
||||||
|
elem.bounds = ctx.position_element(parent, tot_size, style);
|
||||||
|
if (elem.bounds.is_null()) return {};
|
||||||
elem.events = ctx.get_elem_events(elem);
|
elem.events = ctx.get_elem_events(elem);
|
||||||
|
Rect content_bounds = elem.content_bounds(style);
|
||||||
bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover;
|
|
||||||
Style* style = is_active ? style_active : style_norm;
|
Rect text_bounds = {
|
||||||
|
.x = content_bounds.x + icon_size.w + left_pad + inner_pad,
|
||||||
// Draw the button
|
.y = content_bounds.y,
|
||||||
ctx.push_rect(elem.bounds, parent.div.z_index, style)!;
|
.w = content_bounds.w - icon_size.w - left_pad - inner_pad - right_pad,
|
||||||
ctx.push_string(text_size, label, parent.div.z_index, style.fg)!;
|
.h = content_bounds.h
|
||||||
|
};
|
||||||
return elem.events;
|
text_bounds = text_size.center_to(text_bounds);
|
||||||
}
|
|
||||||
|
Rect icon_bounds = {
|
||||||
macro Ctx.button_icon(&ctx, String icon, String on_icon = "", bool active = false, ...)
|
.x = content_bounds.x,
|
||||||
=> ctx.button_icon_id(@compute_id($vasplat), icon, on_icon, active);
|
.y = content_bounds.y,
|
||||||
fn ElemEvents? Ctx.button_icon_id(&ctx, Id id, String icon, String on_icon, bool active)
|
.w = icon_size.w + right_pad + inner_pad,
|
||||||
{
|
.h = (short)max(icon_size.h, content_bounds.h)
|
||||||
id = ctx.gen_id(id)!;
|
};
|
||||||
|
icon_bounds = icon_size.center_to(icon_bounds);
|
||||||
Elem *parent = ctx.get_parent()!;
|
|
||||||
Elem *elem = ctx.get_elem(id, ETYPE_BUTTON)!;
|
bool is_active = ctx.elem_focus(elem) || elem.events.mouse_hover;
|
||||||
|
Style s = *style;
|
||||||
Sprite* def_sprite = ctx.sprite_atlas.get(icon)!;
|
if (is_active) {
|
||||||
Sprite* on_sprite = ctx.sprite_atlas.get(on_icon) ?? &&(Sprite){};
|
s.secondary = s.primary;
|
||||||
Rect max_size = def_sprite.rect().max(on_sprite.rect());
|
s.bg = s.accent;
|
||||||
|
|
||||||
Style* style_norm = ctx.styles.get_style(@str_hash("button"));
|
|
||||||
|
|
||||||
elem.bounds = ctx.position_element(parent, max_size, style_norm);
|
|
||||||
|
|
||||||
// if the bounds are null the element is outside the div view,
|
|
||||||
// no interaction should occur so just return
|
|
||||||
if (elem.bounds.is_null()) { return {}; }
|
|
||||||
|
|
||||||
Style* style_active = ctx.styles.get_style(@str_hash("button-active"));
|
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
|
||||||
|
|
||||||
bool is_active = active || ctx.elem_focus(elem) || elem.events.mouse_hover;
|
|
||||||
Style* style = is_active ? style_active : style_norm;
|
|
||||||
|
|
||||||
Id tex_id = ctx.sprite_atlas.id;
|
|
||||||
if (active && on_icon != "") {
|
|
||||||
ctx.push_sprite(elem.bounds, on_sprite.uv(), tex_id, parent.div.z_index, type: on_sprite.type)!;
|
|
||||||
} else {
|
|
||||||
ctx.push_sprite(elem.bounds, def_sprite.uv(), tex_id, parent.div.z_index, type: def_sprite.type)!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the button
|
ctx.push_rect(elem.bounds, parent.div.z_index, &s)!;
|
||||||
ctx.push_rect(elem.bounds, parent.div.z_index, style)!;
|
ctx.push_sprite(icon_bounds, sprite.uv(), ctx.sprite_atlas.id, parent.div.z_index, type: sprite.type)!;
|
||||||
|
// Style ss = {.bg = 0x000000ffu.@to_rgba()};
|
||||||
|
// ctx.push_rect(content_bounds, parent.div.z_index, &ss)!;
|
||||||
|
ctx.push_string(text_bounds, label, parent.div.z_index, style.fg)!;
|
||||||
|
|
||||||
return elem.events;
|
return elem.events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// FIXME: this should be inside the style
|
// FIXME: this should be inside the style
|
||||||
macro Ctx.checkbox(&ctx, String desc, Point off, bool* active, String tick_sprite = {}, ...)
|
macro Ctx.checkbox(&ctx, String desc, Point off, bool* active, String tick_sprite = {}, ...)
|
||||||
=> ctx.checkbox_id(@compute_id($vasplat), desc, off, active, tick_sprite);
|
=> ctx.checkbox_id(@compute_id($vasplat), desc, off, active, tick_sprite);
|
||||||
|
@ -93,7 +93,6 @@ fn void? Ctx.push_scissor(&ctx, Rect rect, int z_index)
|
|||||||
fn void? Ctx.push_rect(&ctx, Rect rect, int z_index, Style* style)
|
fn void? Ctx.push_rect(&ctx, Rect rect, int z_index, Style* style)
|
||||||
{
|
{
|
||||||
Rect border = style.border;
|
Rect border = style.border;
|
||||||
Rect padding = style.padding;
|
|
||||||
ushort radius = style.radius;
|
ushort radius = style.radius;
|
||||||
Color bg = style.bg;
|
Color bg = style.bg;
|
||||||
Color border_color = style.secondary;
|
Color border_color = style.secondary;
|
||||||
@ -111,10 +110,10 @@ fn void? Ctx.push_rect(&ctx, Rect rect, int z_index, Style* style)
|
|||||||
Cmd cmd = {
|
Cmd cmd = {
|
||||||
.type = CMD_RECT,
|
.type = CMD_RECT,
|
||||||
.rect.rect = {
|
.rect.rect = {
|
||||||
.x = rect.x + border.x + padding.x,
|
.x = rect.x + border.x,
|
||||||
.y = rect.y + border.y + padding.y,
|
.y = rect.y + border.y,
|
||||||
.w = rect.w - (border.x+border.w) - (padding.x+padding.w),
|
.w = rect.w - (border.x+border.w),
|
||||||
.h = rect.h - (border.y+border.h) - (padding.y+padding.h),
|
.h = rect.h - (border.y+border.h),
|
||||||
},
|
},
|
||||||
.rect.color = bg,
|
.rect.color = bg,
|
||||||
.rect.radius = radius,
|
.rect.radius = radius,
|
||||||
@ -123,6 +122,7 @@ fn void? Ctx.push_rect(&ctx, Rect rect, int z_index, Style* style)
|
|||||||
ctx.push_cmd(&cmd, z_index)!;
|
ctx.push_cmd(&cmd, z_index)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: accept a Sprite* instead of all this shit
|
||||||
fn void? Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, int z_index, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL)
|
fn void? Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, int z_index, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL)
|
||||||
{
|
{
|
||||||
Cmd cmd = {
|
Cmd cmd = {
|
||||||
|
@ -78,6 +78,7 @@ fn void? Ctx.layout_next_column(&ctx)
|
|||||||
parent.div.origin_r = parent.div.origin_c;
|
parent.div.origin_r = parent.div.origin_c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro Rect Elem.content_bounds(&elem, style) => elem.bounds.pad(style.border).pad(style.padding);
|
||||||
|
|
||||||
macro Rect Elem.get_view(&elem)
|
macro Rect Elem.get_view(&elem)
|
||||||
{
|
{
|
||||||
|
@ -156,8 +156,22 @@ macro Point Rect.bottom_right(Rect r)
|
|||||||
|
|
||||||
macro Rect Rect.center_to(Rect a, Rect b)
|
macro Rect Rect.center_to(Rect a, Rect b)
|
||||||
{
|
{
|
||||||
Point off = {.x = (b.w - a.w)/2, .y = (b.h - a.h)/2};
|
return {
|
||||||
return a.off(off);
|
.x = b.x + (b.w - a.w)/2,
|
||||||
|
.y = b.y + (b.h - a.h)/2,
|
||||||
|
.w = a.w,
|
||||||
|
.h = a.h,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro Rect Rect.pad(Rect a, Rect b)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
.x = a.x + b.x,
|
||||||
|
.y = a.y + b.y,
|
||||||
|
.w = a.w - b.x - b.w,
|
||||||
|
.h = a.h - b.y - b.h,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -9,27 +9,15 @@ default {
|
|||||||
button {
|
button {
|
||||||
margin: 2 2 2 2;
|
margin: 2 2 2 2;
|
||||||
border: 2 2 2 2;
|
border: 2 2 2 2;
|
||||||
padding: 1 1 1 1;
|
padding: 2 2 2 2;
|
||||||
radius: 10;
|
radius: 10;
|
||||||
|
size: 32;
|
||||||
|
|
||||||
bg: #3c3836ff;
|
bg: #3c3836ff;
|
||||||
fg: #fbf1c7ff;
|
fg: #fbf1c7ff;
|
||||||
primary: #cc241dff;
|
primary: #cc241dff;
|
||||||
secondary: #458588ff;
|
secondary: #458588ff;
|
||||||
accent: #fabd2fff;
|
accent: #504945ff;
|
||||||
}
|
|
||||||
|
|
||||||
button-active {
|
|
||||||
margin: 2 2 2 2;
|
|
||||||
border: 2 2 2 2;
|
|
||||||
padding: 1 1 1 1;
|
|
||||||
radius: 10;
|
|
||||||
|
|
||||||
bg: #504945ff;
|
|
||||||
fg: #fbf1c7ff;
|
|
||||||
primary: #cc241dff;
|
|
||||||
secondary: #cc241dff;
|
|
||||||
accent: #fabd2fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkbox {
|
checkbox {
|
||||||
|
24
src/main.c3
24
src/main.c3
@ -202,16 +202,16 @@ fn int main(String[] args)
|
|||||||
ui.div_begin({.w=-100})!!;
|
ui.div_begin({.w=-100})!!;
|
||||||
{
|
{
|
||||||
ui.layout_set_column()!!;
|
ui.layout_set_column()!!;
|
||||||
if (ui.button({0,0,30,30}, toggle)!!.mouse_press) {
|
if (ui.button(icon: "tux")!!.mouse_press) {
|
||||||
io::printn("press button0");
|
io::printn("press button0");
|
||||||
toggle = !toggle;
|
toggle = !toggle;
|
||||||
}
|
}
|
||||||
//ui.layout_next_column()!!;
|
//ui.layout_next_column()!!;
|
||||||
if (ui.button({0,0,30,30})!!.mouse_press) {
|
if (ui.button(label: "ciao", icon: "tick")!!.mouse_press) {
|
||||||
io::printn("press button1");
|
io::printn("press button1");
|
||||||
}
|
}
|
||||||
//ui.layout_next_column()!!;
|
//ui.layout_next_column()!!;
|
||||||
if (ui.button({0,0,30,30})!!.mouse_release) {
|
if (ui.button()!!.mouse_release) {
|
||||||
io::printn("release button2");
|
io::printn("release button2");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ fn int main(String[] args)
|
|||||||
ui.text_unbounded("Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!;
|
ui.text_unbounded("Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!;
|
||||||
|
|
||||||
ui.layout_next_column()!!;
|
ui.layout_next_column()!!;
|
||||||
ui.button_label("Continua!")!!;
|
ui.button("Continua!")!!;
|
||||||
|
|
||||||
ui.layout_next_row()!!;
|
ui.layout_next_row()!!;
|
||||||
static bool check;
|
static bool check;
|
||||||
@ -250,15 +250,15 @@ fn int main(String[] args)
|
|||||||
if (ui.slider_ver({0,0,30,100}, &slider2)!!.update) {
|
if (ui.slider_ver({0,0,30,100}, &slider2)!!.update) {
|
||||||
io::printfn("other slider: %f", slider2);
|
io::printfn("other slider: %f", slider2);
|
||||||
}
|
}
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
ui.button({0,0,50,50})!!;
|
ui.button()!!;
|
||||||
}
|
}
|
||||||
ui.layout_next_column()!!;
|
ui.layout_next_column()!!;
|
||||||
ui.layout_set_row()!!;
|
ui.layout_set_row()!!;
|
||||||
|
Loading…
Reference in New Issue
Block a user