working example of the new layout system
This commit is contained in:
parent
2619873ca7
commit
0f7d5a6506
@ -218,3 +218,22 @@ at frame end:
|
|||||||
Styling
|
Styling
|
||||||
=======
|
=======
|
||||||
|
|
||||||
|
+-----------------------------------------+
|
||||||
|
| MARGIN |
|
||||||
|
| |
|
||||||
|
| +-----------------------------------+ |
|
||||||
|
| |xxxxxxxxxxx BORDER xxxxxxxxxxxx| |
|
||||||
|
| |x+-------------------------------+x| |
|
||||||
|
| |x| PADDING |x| |
|
||||||
|
| |x| +-----------------------+ |x| |
|
||||||
|
| |x| | | |x| |
|
||||||
|
| |x| | CONTENT | |x| |
|
||||||
|
| |x| +-----------------------+ |x| |
|
||||||
|
| |x| |x| |
|
||||||
|
| |x+-------------------------------+x| |
|
||||||
|
| |xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx| |
|
||||||
|
| +-----------------------------------+ |
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
+-----------------------------------------+
|
||||||
|
|
||||||
|
@ -19,13 +19,14 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
|
|
||||||
Sprite* sprite = icon != "" ? ctx.sprite_atlas.get(icon)! : &&(Sprite){};
|
Sprite* sprite = icon != "" ? ctx.sprite_atlas.get(icon)! : &&(Sprite){};
|
||||||
|
|
||||||
Rect min_size = {0, 0, style.size, style.size};
|
// TODO: get min size by style
|
||||||
Rect text_size = ctx.get_text_bounds(label)!;
|
TextSize text_size = ctx.measure_string(label)!;
|
||||||
Rect icon_size = sprite.rect();
|
Rect icon_size = sprite.rect();
|
||||||
|
|
||||||
ushort h_lh = (ushort)(ctx.font.line_height() / 2);
|
ushort min_size = style.size;
|
||||||
ushort left_pad = label != "" ? h_lh : 0;
|
ushort half_lh = (ushort)(ctx.font.line_height() / 2);
|
||||||
ushort inner_pad = label != "" && icon != "" ? h_lh : 0;
|
ushort left_pad = label != "" ? half_lh : 0;
|
||||||
|
ushort inner_pad = label != "" && icon != "" ? half_lh : 0;
|
||||||
ushort right_pad = left_pad;
|
ushort right_pad = left_pad;
|
||||||
|
|
||||||
/* |left_pad
|
/* |left_pad
|
||||||
@ -39,13 +40,25 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
* |inner_pad |right_pad
|
* |inner_pad |right_pad
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Rect tot_size = {
|
elem.layout.w = {
|
||||||
.w = (short)max(min_size.w, icon_size.w + text_size.w + left_pad + inner_pad + right_pad),
|
.min = (short)max(min_size, left_pad + icon_size.w + inner_pad + text_size.width.min + right_pad),
|
||||||
.h = (short)max(min_size.h, max(icon_size.h, text_size.h)),
|
.max = (short)max(min_size, left_pad + icon_size.w + inner_pad + text_size.width.max + right_pad),
|
||||||
};
|
};
|
||||||
|
elem.layout.h = {
|
||||||
|
.min = (short)max(min_size, left_pad + icon_size.h + text_size.height.min + right_pad),
|
||||||
|
.max = (short)max(min_size, left_pad + icon_size.w + text_size.height.max + right_pad),
|
||||||
|
};
|
||||||
|
// add style border and padding
|
||||||
|
elem.layout.margin = style.margin;
|
||||||
|
elem.layout.border = style.border;
|
||||||
|
elem.layout.padding = style.padding;
|
||||||
|
|
||||||
|
elem.layout.children.w = elem.layout.w;
|
||||||
|
elem.layout.children.h = elem.layout.h;
|
||||||
|
|
||||||
|
update_parent_grow(elem, parent);
|
||||||
|
update_parent_size(elem, parent);
|
||||||
|
|
||||||
elem.bounds = ctx.layout_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);
|
Rect content_bounds = elem.content_bounds(style);
|
||||||
|
|
||||||
@ -55,7 +68,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
.w = content_bounds.w - icon_size.w - left_pad - inner_pad - right_pad,
|
.w = content_bounds.w - icon_size.w - left_pad - inner_pad - right_pad,
|
||||||
.h = content_bounds.h
|
.h = content_bounds.h
|
||||||
};
|
};
|
||||||
text_bounds = text_size.center_to(text_bounds);
|
//text_bounds = text_size.center_to(text_bounds);
|
||||||
|
|
||||||
Rect icon_bounds = {
|
Rect icon_bounds = {
|
||||||
.x = content_bounds.x,
|
.x = content_bounds.x,
|
||||||
@ -82,6 +95,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
|||||||
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 = {}, ...)
|
||||||
@ -152,3 +166,4 @@ fn void? Ctx.toggle_id(&ctx, Id id, String description, Point off, bool* active)
|
|||||||
s.margin = s.border = s.padding = {};
|
s.margin = s.border = s.padding = {};
|
||||||
ctx.push_rect(t, parent.div.z_index, &s)!;
|
ctx.push_rect(t, parent.div.z_index, &s)!;
|
||||||
}
|
}
|
||||||
|
*/
|
@ -97,9 +97,9 @@ macro Ctx.push_cmd(&ctx, Cmd *cmd, int z_index)
|
|||||||
default: return ctx.cmd_queue.enqueue(cmd);
|
default: return ctx.cmd_queue.enqueue(cmd);
|
||||||
}
|
}
|
||||||
if (cull_rect(rect, ctx.div_scissor)) {
|
if (cull_rect(rect, ctx.div_scissor)) {
|
||||||
io::print("NOPE: ");
|
// io::print("NOPE: ");
|
||||||
io::print(cmd.rect.rect);
|
// io::print(cmd.rect.rect);
|
||||||
io::printn(cmd.z_index);
|
// io::printn(cmd.z_index);
|
||||||
// unreachable();
|
// unreachable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,15 @@ import std::core::string;
|
|||||||
import std::core::mem::allocator;
|
import std::core::mem::allocator;
|
||||||
|
|
||||||
|
|
||||||
|
macro println(...)
|
||||||
|
{
|
||||||
|
$for var $i = 0; $i < $vacount; $i++:
|
||||||
|
io::print($vaexpr[$i]);
|
||||||
|
$endfor
|
||||||
|
io::printn();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// element ids are just long ints
|
// element ids are just long ints
|
||||||
alias Id = uint;
|
alias Id = uint;
|
||||||
|
|
||||||
@ -45,7 +54,9 @@ struct Elem {
|
|||||||
ElemFlags flags;
|
ElemFlags flags;
|
||||||
ElemEvents events;
|
ElemEvents events;
|
||||||
Rect bounds;
|
Rect bounds;
|
||||||
|
Rect children_bounds;
|
||||||
ElemType type;
|
ElemType type;
|
||||||
|
Layout layout;
|
||||||
union {
|
union {
|
||||||
ElemDiv div;
|
ElemDiv div;
|
||||||
ElemButton button;
|
ElemButton button;
|
||||||
@ -131,6 +142,7 @@ const uint GOLDEN_RATIO = 0x9E3779B9;
|
|||||||
// with the Cantor pairing function
|
// with the Cantor pairing function
|
||||||
fn Id? Ctx.gen_id(&ctx, Id id2)
|
fn Id? Ctx.gen_id(&ctx, Id id2)
|
||||||
{
|
{
|
||||||
|
// FIXME: this is SHIT
|
||||||
Id id1 = ctx.tree.get(ctx.active_div)!;
|
Id id1 = ctx.tree.get(ctx.active_div)!;
|
||||||
// Mix the two IDs non-linearly
|
// Mix the two IDs non-linearly
|
||||||
Id mixed = id1 ^ id2.rotate_left(13);
|
Id mixed = id1 ^ id2.rotate_left(13);
|
||||||
@ -160,6 +172,7 @@ fn Elem*? Ctx.get_elem(&ctx, Id id, ElemType type)
|
|||||||
elem.flags = (ElemFlags)0;
|
elem.flags = (ElemFlags)0;
|
||||||
elem.flags.is_new = is_new;
|
elem.flags.is_new = is_new;
|
||||||
elem.id = id;
|
elem.id = id;
|
||||||
|
elem.layout = {};
|
||||||
if (is_new == false && elem.type != type) {
|
if (is_new == false && elem.type != type) {
|
||||||
return WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
} else {
|
} else {
|
||||||
@ -232,16 +245,15 @@ fn void? Ctx.frame_begin(&ctx)
|
|||||||
//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};
|
||||||
elem.div.layout = LAYOUT_ROW;
|
|
||||||
elem.div.z_index = 0;
|
elem.div.z_index = 0;
|
||||||
elem.div.children_bounds = elem.bounds;
|
|
||||||
elem.div.scroll_x.enabled = false;
|
elem.div.scroll_x.enabled = false;
|
||||||
elem.div.scroll_y.enabled = false;
|
elem.div.scroll_y.enabled = false;
|
||||||
elem.div.pcb = {};
|
elem.layout.dir = ROW;
|
||||||
elem.div.origin_c = {};
|
elem.layout.anchor = TOP_LEFT;
|
||||||
elem.div.origin_r = {};
|
elem.layout.w = @exact(ctx.width);
|
||||||
|
elem.layout.h = @exact(ctx.height);
|
||||||
|
|
||||||
ctx.div_scissor = {0, 0, ctx.width, ctx.height};
|
ctx.div_scissor = elem.bounds;
|
||||||
|
|
||||||
// The root element does not push anything to the stack
|
// The root element does not push anything to the stack
|
||||||
// TODO: add a background color taken from a theme or config
|
// TODO: add a background color taken from a theme or config
|
||||||
@ -253,7 +265,13 @@ 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
|
// 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) return WRONG_ID?;
|
if (root.id != ROOT_ID) {
|
||||||
|
io::printn(root.id);
|
||||||
|
return WRONG_ID?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DO THE LAYOUT
|
||||||
|
ctx.layout_element_tree();
|
||||||
|
|
||||||
// 1. clear the tree
|
// 1. clear the tree
|
||||||
ctx.tree.nuke();
|
ctx.tree.nuke();
|
||||||
|
@ -5,7 +5,6 @@ import std::math;
|
|||||||
|
|
||||||
// div element
|
// div element
|
||||||
struct ElemDiv {
|
struct ElemDiv {
|
||||||
Layout layout;
|
|
||||||
struct scroll_x {
|
struct scroll_x {
|
||||||
bool enabled;
|
bool enabled;
|
||||||
bool on;
|
bool on;
|
||||||
@ -18,28 +17,42 @@ struct ElemDiv {
|
|||||||
}
|
}
|
||||||
ushort scroll_size;
|
ushort scroll_size;
|
||||||
int z_index;
|
int z_index;
|
||||||
Rect children_bounds; // current frame children bounds
|
|
||||||
Rect pcb; // previous frame children bounds
|
|
||||||
Point origin_r, origin_c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// useful macro to start and end a div, capturing the trailing block
|
// useful macro to start and end a div, capturing the trailing block
|
||||||
macro Ctx.@div(&ctx, Rect size, bool scroll_x = false, bool scroll_y = false, ...; @body())
|
macro Ctx.@div(&ctx,
|
||||||
|
Size width = @grow, Size height = @grow,
|
||||||
|
LayoutDirection dir = ROW,
|
||||||
|
Anchor anchor = TOP_LEFT,
|
||||||
|
bool scroll_x = false, bool scroll_y = false,
|
||||||
|
...;
|
||||||
|
@body()
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ctx.div_begin(size, scroll_x, scroll_y, $vasplat)!;
|
ctx.div_begin(width, height, dir, anchor, scroll_x, scroll_y, $vasplat)!;
|
||||||
@body();
|
@body();
|
||||||
ctx.div_end()!;
|
ctx.div_end()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
// begin a widget container, or div, the size determines the offset (x,y) width and height.
|
macro Ctx.div_begin(&ctx,
|
||||||
// if the width or height are zero the width or height are set to the maximum available.
|
Size width = @grow(), Size height = @grow(),
|
||||||
// if the width or height are negative the width or height will be calculated based on the children size
|
LayoutDirection dir = ROW,
|
||||||
// sort similar to a flexbox, and the minimum size is set by the negative of the width or height
|
Anchor anchor = TOP_LEFT,
|
||||||
// FIXME: there is a bug if the size.w or size.h == -0
|
bool scroll_x = false, bool scroll_y = false,
|
||||||
macro Ctx.div_begin(&ctx, Rect size, bool scroll_x = false, bool scroll_y = false, ...)
|
...
|
||||||
=> ctx.div_begin_id(@compute_id($vasplat), size, scroll_x, scroll_y);
|
)
|
||||||
fn void? Ctx.div_begin_id(&ctx, Id id, Rect size, bool scroll_x, bool scroll_y)
|
{
|
||||||
|
return ctx.div_begin_id(@compute_id($vasplat), width, height, dir, anchor, scroll_x, scroll_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void? Ctx.div_begin_id(&ctx,
|
||||||
|
Id id,
|
||||||
|
Size width, Size height,
|
||||||
|
LayoutDirection dir,
|
||||||
|
Anchor anchor,
|
||||||
|
bool scroll_x, bool scroll_y
|
||||||
|
)
|
||||||
{
|
{
|
||||||
id = ctx.gen_id(id)!;
|
id = ctx.gen_id(id)!;
|
||||||
|
|
||||||
@ -55,30 +68,24 @@ fn void? Ctx.div_begin_id(&ctx, Id id, Rect size, bool scroll_x, bool scroll_y)
|
|||||||
elem.div.scroll_size = slider_style.size ? slider_style.size : (style.size ? style.size : DEFAULT_STYLE.size);
|
elem.div.scroll_size = slider_style.size ? slider_style.size : (style.size ? style.size : DEFAULT_STYLE.size);
|
||||||
elem.div.z_index = parent.div.z_index + 1;
|
elem.div.z_index = parent.div.z_index + 1;
|
||||||
|
|
||||||
// 2. layout the element
|
|
||||||
Rect wanted_size = {
|
|
||||||
.x = size.x,
|
|
||||||
.y = size.y,
|
|
||||||
.w = size.w < 0 ? max(elem.div.pcb.w, (short)-size.w) : size.w,
|
|
||||||
.h = size.h < 0 ? max(elem.div.pcb.h, (short)-size.h) : size.h,
|
|
||||||
};
|
|
||||||
elem.bounds = ctx.layout_element(parent, wanted_size, style);
|
|
||||||
elem.div.children_bounds = {.x = elem.bounds.x, .y = elem.bounds.y};
|
|
||||||
|
|
||||||
// update the ctx scissor
|
// update the ctx scissor
|
||||||
ctx.div_scissor = elem.bounds;
|
ctx.div_scissor = elem.bounds;
|
||||||
ctx.push_scissor(elem.bounds, elem.div.z_index)!;
|
ctx.push_scissor(elem.bounds, elem.div.z_index)!;
|
||||||
|
|
||||||
// 4. Fill the div fields
|
// update layout with correct info
|
||||||
elem.div.origin_c = {
|
elem.layout = {
|
||||||
.x = elem.bounds.x,
|
.w = width,
|
||||||
.y = elem.bounds.y
|
.h = height,
|
||||||
|
.dir = dir,
|
||||||
|
.anchor = anchor,
|
||||||
|
.margin = style.margin,
|
||||||
|
.border = style.border,
|
||||||
|
.padding = style.padding,
|
||||||
};
|
};
|
||||||
elem.div.origin_r = elem.div.origin_c;
|
|
||||||
elem.div.layout = parent.div.layout;
|
|
||||||
|
|
||||||
// Add the background to the draw stack
|
// update parent grow children
|
||||||
bool do_border = parent.div.layout == LAYOUT_FLOATING;
|
update_parent_grow(elem, parent);
|
||||||
|
|
||||||
ctx.push_rect(elem.bounds, elem.div.z_index, style)!;
|
ctx.push_rect(elem.bounds, elem.div.z_index, style)!;
|
||||||
|
|
||||||
elem.events = ctx.get_elem_events(elem);
|
elem.events = ctx.get_elem_events(elem);
|
||||||
@ -89,14 +96,9 @@ fn void? Ctx.div_begin_id(&ctx, Id id, Rect size, bool scroll_x, bool scroll_y)
|
|||||||
|
|
||||||
fn void? Ctx.div_end(&ctx)
|
fn void? Ctx.div_end(&ctx)
|
||||||
{
|
{
|
||||||
// swap the children bounds
|
|
||||||
Elem* elem = ctx.get_active_div()!;
|
Elem* elem = ctx.get_active_div()!;
|
||||||
elem.div.pcb = elem.div.children_bounds;
|
|
||||||
|
|
||||||
// FIXME: this causes all elements inside the div to loose focus since the mouse press happens
|
|
||||||
// both inside the element and inside the div bounds
|
|
||||||
//elem.events = ctx.get_elem_events(elem);
|
|
||||||
|
|
||||||
|
/* FIXME: Redo slider functionality
|
||||||
Rect cb = elem.div.pcb;
|
Rect cb = elem.div.pcb;
|
||||||
// children bounds bottom-right corner
|
// children bounds bottom-right corner
|
||||||
Point cbc = {
|
Point cbc = {
|
||||||
@ -155,10 +157,13 @@ fn void? Ctx.div_end(&ctx)
|
|||||||
ctx.slider_hor_id(hsid_raw, hslider, &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15))!;
|
ctx.slider_hor_id(hsid_raw, hslider, &elem.div.scroll_x.value, max((float)bc.x / cbc.x, (float)0.15))!;
|
||||||
elem.div.layout = prev_l;
|
elem.div.layout = prev_l;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// the active_div returns to the parent of the current one
|
// the active_div returns to the parent of the current one
|
||||||
ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
|
ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
|
||||||
Elem* parent = ctx.get_parent()!;
|
Elem* parent = ctx.get_parent()!;
|
||||||
// TODO: reset the scissor back to the parent div
|
|
||||||
ctx.div_scissor = parent.bounds;
|
ctx.div_scissor = parent.bounds;
|
||||||
|
ctx.push_scissor(parent.bounds, elem.div.z_index)!;
|
||||||
|
|
||||||
|
update_parent_size(elem, parent);
|
||||||
}
|
}
|
||||||
|
@ -9,11 +9,34 @@ import std::io;
|
|||||||
import std::ascii;
|
import std::ascii;
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
// CODEPOINT //
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
// unicode code point, different type for a different hash
|
// unicode code point, different type for a different hash
|
||||||
alias Codepoint = uint;
|
alias Codepoint = uint;
|
||||||
|
|
||||||
|
<*
|
||||||
|
@require off != null
|
||||||
|
@require str.ptr != null
|
||||||
|
*>
|
||||||
|
fn Codepoint str_to_codepoint(char[] str, usz* off)
|
||||||
|
{
|
||||||
|
Codepoint cp;
|
||||||
|
isz b = grapheme::decode_utf8(str, str.len, (uint*)&cp);
|
||||||
|
if (b == 0 || b > str.len) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
*off = b;
|
||||||
|
return cp;
|
||||||
|
}
|
||||||
|
|
||||||
//macro uint Codepoint.hash(self) => ((uint)self).hash();
|
//macro uint Codepoint.hash(self) => ((uint)self).hash();
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
// FONT ATLAS //
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
/* width and height of a glyph contain the kering advance
|
/* width and height of a glyph contain the kering advance
|
||||||
* (u,v)
|
* (u,v)
|
||||||
* +-------------*---+ -
|
* +-------------*---+ -
|
||||||
@ -60,7 +83,7 @@ struct Font {
|
|||||||
|
|
||||||
fn void? Font.load(&font, String name, ZString path, uint height, float scale)
|
fn void? Font.load(&font, String name, ZString path, uint height, float scale)
|
||||||
{
|
{
|
||||||
font.table.init(allocator::heap(), capacity: FONT_CACHED);
|
font.table.init(allocator::mem, capacity: FONT_CACHED);
|
||||||
font.id = name.hash();
|
font.id = name.hash();
|
||||||
|
|
||||||
font.size = height*scale;
|
font.size = height*scale;
|
||||||
@ -160,26 +183,39 @@ fn void Font.free(&font)
|
|||||||
schrift::freefont(font.sft.font);
|
schrift::freefont(font.sft.font);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
// FONT LOAD AND QUERY //
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
fn void? Ctx.load_font(&ctx, String name, ZString path, uint height, float scale = 1.0)
|
fn void? Ctx.load_font(&ctx, String name, ZString path, uint height, float scale = 1.0)
|
||||||
{
|
{
|
||||||
return ctx.font.load(name, path, height, scale);
|
return ctx.font.load(name, path, height, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
<*
|
// TODO: check if the font is present in the context
|
||||||
@require off != null
|
fn Id Ctx.get_font_id(&ctx, String label)
|
||||||
@require str.ptr != null
|
|
||||||
*>
|
|
||||||
fn Codepoint str_to_codepoint(char[] str, usz* off)
|
|
||||||
{
|
{
|
||||||
Codepoint cp;
|
return (Id)label.hash();
|
||||||
isz b = grapheme::decode_utf8(str, str.len, (uint*)&cp);
|
|
||||||
if (b == 0 || b > str.len) {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
*off = b;
|
|
||||||
return cp;
|
fn Atlas*? Ctx.get_font_atlas(&ctx, String name)
|
||||||
|
{
|
||||||
|
// TODO: use the font name, for now there is only one font
|
||||||
|
if (name.hash() != ctx.font.id) {
|
||||||
|
return WRONG_ID?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &ctx.font.atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn int Font.line_height(&font) => (int)(font.ascender - font.descender + (float)0.5);
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
// TEXT MEASUREMENT //
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
|
||||||
const uint TAB_SIZE = 4;
|
const uint TAB_SIZE = 4;
|
||||||
|
|
||||||
// TODO: change the name
|
// TODO: change the name
|
||||||
@ -189,6 +225,7 @@ struct TextInfo {
|
|||||||
Rect bounds;
|
Rect bounds;
|
||||||
String text;
|
String text;
|
||||||
usz off;
|
usz off;
|
||||||
|
Size width, height;
|
||||||
|
|
||||||
// current glyph info
|
// current glyph info
|
||||||
Point origin;
|
Point origin;
|
||||||
@ -295,20 +332,81 @@ fn Rect? Ctx.get_text_bounds(&ctx, String text)
|
|||||||
return ti.text_bounds;
|
return ti.text_bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if the font is present in the context
|
|
||||||
fn Id Ctx.get_font_id(&ctx, String label)
|
struct TextSize {
|
||||||
|
Size width, height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measeure the size of a string.
|
||||||
|
// width.min: as if each word is broken up by a new line
|
||||||
|
// width.max: the width of the string left as-is
|
||||||
|
// height.min: the height of the string left as-is
|
||||||
|
// height.max: the height of the string with each word broken up by a new line
|
||||||
|
fn TextSize? Ctx.measure_string(&ctx, String text)
|
||||||
{
|
{
|
||||||
return (Id)label.hash();
|
Font* font = &ctx.font;
|
||||||
|
short baseline = (short)font.ascender;
|
||||||
|
short line_height = (short)font.line_height();
|
||||||
|
short line_gap = (short)font.linegap;
|
||||||
|
short space_width = font.get_glyph(' ').adv!;
|
||||||
|
short tab_width = space_width * TAB_SIZE;
|
||||||
|
|
||||||
|
isz off;
|
||||||
|
usz x;
|
||||||
|
|
||||||
|
TextSize ts;
|
||||||
|
|
||||||
|
short word_width;
|
||||||
|
short words = 1;
|
||||||
|
Rect bounds; // unaltered text bounds;
|
||||||
|
Point origin;
|
||||||
|
|
||||||
|
Codepoint cp = str_to_codepoint(text[off..], &x);
|
||||||
|
for (; cp != 0; cp = str_to_codepoint(text[off..], &x)) {
|
||||||
|
off += x;
|
||||||
|
Glyph* gp = font.get_glyph(cp)!;
|
||||||
|
|
||||||
|
// update the text bounds
|
||||||
|
switch {
|
||||||
|
case cp == '\n':
|
||||||
|
origin.x = 0;
|
||||||
|
origin.y += line_height + line_gap;
|
||||||
|
case cp == '\t':
|
||||||
|
origin.x += tab_width;
|
||||||
|
case ascii::is_cntrl((char)cp):
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Rect b = {
|
||||||
|
.x = origin.x + gp.ox,
|
||||||
|
.y = origin.y + gp.oy + baseline,
|
||||||
|
.w = gp.w,
|
||||||
|
.h = gp.h,
|
||||||
|
};
|
||||||
|
bounds = containing_rect(bounds, b);
|
||||||
|
origin.x += gp.adv;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Atlas*? Ctx.get_font_atlas(&ctx, String name)
|
// update the word width
|
||||||
{
|
switch {
|
||||||
// TODO: use the font name, for now there is only one font
|
case ascii::is_space((char)cp):
|
||||||
if (name.hash() != ctx.font.id) {
|
if (word_width > ts.width.min) ts.width.min = word_width;
|
||||||
return WRONG_ID?;
|
word_width = 0;
|
||||||
|
words++;
|
||||||
|
default:
|
||||||
|
word_width += gp.w + gp.ox;
|
||||||
|
if (off < text.len) word_width += gp.adv;
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ctx.font.atlas;
|
|
||||||
}
|
}
|
||||||
|
// end of string is also end of word
|
||||||
|
if (word_width > ts.width.min) ts.width.min = word_width;
|
||||||
|
|
||||||
fn int Font.line_height(&font) => (int)(font.ascender - font.descender + (float)0.5);
|
ts.width.max = bounds.w;
|
||||||
|
ts.height.min = bounds.h;
|
||||||
|
ts.height.max = words * line_height + line_gap * (words-1);
|
||||||
|
|
||||||
|
//io::print("'");
|
||||||
|
//io::print(text);
|
||||||
|
//io::print("': ");
|
||||||
|
//io::printn(ts);
|
||||||
|
return ts;
|
||||||
|
}
|
||||||
|
@ -1,54 +1,102 @@
|
|||||||
module ugui;
|
module ugui;
|
||||||
|
|
||||||
enum Layout {
|
import std::math;
|
||||||
LAYOUT_ROW,
|
import std::io;
|
||||||
LAYOUT_COLUMN,
|
|
||||||
LAYOUT_FLOATING,
|
enum LayoutDirection {
|
||||||
LAYOUT_ABSOLUTE,
|
ROW,
|
||||||
|
COLUMN,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.layout_set_row(&ctx)
|
enum Anchor {
|
||||||
{
|
TOP_LEFT,
|
||||||
Elem *parent = ctx.get_parent()!;
|
LEFT,
|
||||||
parent.div.layout = LAYOUT_ROW;
|
BOTTOM_LEFT,
|
||||||
|
BOTTOM,
|
||||||
|
BOTTOM_RIGHT,
|
||||||
|
RIGHT,
|
||||||
|
TOP_RIGHT,
|
||||||
|
TOP,
|
||||||
|
CENTER
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.layout_set_column(&ctx)
|
struct Layout {
|
||||||
{
|
Size w, h;
|
||||||
Elem *parent = ctx.get_parent()!;
|
struct children {
|
||||||
parent.div.layout = LAYOUT_COLUMN;
|
Size w, h;
|
||||||
|
}
|
||||||
|
ushort grow_children;
|
||||||
|
short occupied;
|
||||||
|
struct origin {
|
||||||
|
short x, y;
|
||||||
|
}
|
||||||
|
LayoutDirection dir;
|
||||||
|
Anchor anchor;
|
||||||
|
// FIXME: all this cruft with margin border and padding could be resolved by simply providing
|
||||||
|
// a "Rect content_offset" that represents the combined effect of all three above
|
||||||
|
Rect margin, border, padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.layout_set_floating(&ctx)
|
// Total width of a layout element, complete with margin, border and padding
|
||||||
|
macro Size Layout.total_width(&el)
|
||||||
{
|
{
|
||||||
Elem *parent = ctx.get_parent()!;
|
Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.margin).expand(el.border).expand(el.padding);
|
||||||
parent.div.layout = LAYOUT_FLOATING;
|
Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.margin).expand(el.border).expand(el.padding);
|
||||||
|
return {.min = min.w, .max = max.w};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.layout_next_row(&ctx)
|
// Total height of a layout element, complete with margin, border and padding
|
||||||
|
macro Size Layout.total_height(&el)
|
||||||
{
|
{
|
||||||
Elem *parent = ctx.get_parent()!;
|
Rect min = (Rect){.w = el.w.min, .h = el.h.min}.expand(el.margin).expand(el.border).expand(el.padding);
|
||||||
|
Rect max = (Rect){.w = el.w.max, .h = el.h.max}.expand(el.margin).expand(el.border).expand(el.padding);
|
||||||
|
return {.min = min.h, .max = max.h};
|
||||||
|
}
|
||||||
|
|
||||||
parent.div.origin_r = {
|
// The content space of the element
|
||||||
.x = parent.bounds.x,
|
macro Point Elem.content_space(&e)
|
||||||
.y = parent.div.children_bounds.bottom_right().y,
|
{
|
||||||
|
return {
|
||||||
|
.x = e.bounds.w - e.layout.border.x - e.layout.border.w - e.layout.padding.x - e.layout.padding.w,
|
||||||
|
.y = e.bounds.h - e.layout.border.y - e.layout.border.h - e.layout.padding.y - e.layout.padding.h,
|
||||||
};
|
};
|
||||||
parent.div.origin_c = parent.div.origin_r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void? Ctx.layout_next_column(&ctx)
|
// Update the parent element's grow children counter
|
||||||
|
fn void update_parent_grow(Elem* child, Elem* parent)
|
||||||
{
|
{
|
||||||
Elem *parent = ctx.get_parent()!;
|
switch (parent.layout.dir) {
|
||||||
|
case ROW:
|
||||||
|
if (child.layout.w.@is_grow()) parent.layout.grow_children++;
|
||||||
|
case COLUMN:
|
||||||
|
if (child.layout.h.@is_grow()) parent.layout.grow_children++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parent.div.origin_c = {
|
// Update the parent element's children size
|
||||||
.x = parent.div.children_bounds.bottom_right().x,
|
fn void update_parent_size(Elem* child, Elem* parent)
|
||||||
.y = parent.bounds.y,
|
{
|
||||||
};
|
Layout* cl = &child.layout;
|
||||||
parent.div.origin_r = parent.div.origin_c;
|
Layout* pl = &parent.layout;
|
||||||
|
|
||||||
|
switch (pl.dir) {
|
||||||
|
case ROW: // on rows grow the ch width by the child width and only grow ch height if it exceeds
|
||||||
|
pl.children.w += cl.total_width();
|
||||||
|
pl.children.h = pl.children.h.comb_max(cl.total_height());
|
||||||
|
case COLUMN: // do the opposite on column
|
||||||
|
pl.children.w = pl.children.w.comb_max(cl.total_width());
|
||||||
|
pl.children.h += cl.total_height();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void update_children_bounds(Elem* child, Elem* parent)
|
||||||
|
{
|
||||||
|
parent.children_bounds = containing_rect(child.bounds, parent.bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Rect Elem.content_bounds(&elem, style) => elem.bounds.pad(style.border).pad(style.padding);
|
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)
|
||||||
{
|
{
|
||||||
Rect off;
|
Rect off;
|
||||||
@ -67,91 +115,266 @@ macro Point Elem.get_view_off(&elem)
|
|||||||
{
|
{
|
||||||
return elem.get_view().sub(elem.bounds).position();
|
return elem.get_view().sub(elem.bounds).position();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// position the rectangle inside the parent according to the layout
|
// Assign the width and height of an element in the directions that it doesn't need to grow
|
||||||
// parent: parent div
|
fn void resolve_dimensions(Elem* e, Elem* p)
|
||||||
// rect: the requested size
|
|
||||||
// style: apply style
|
|
||||||
<*
|
|
||||||
@require ctx != null
|
|
||||||
@require parent.type == ETYPE_DIV
|
|
||||||
*>
|
|
||||||
fn Rect Ctx.layout_element(&ctx, Elem *parent, Rect rect, Style* style)
|
|
||||||
{
|
{
|
||||||
ElemDiv* div = &parent.div;
|
// The element's border and paddign total width and height
|
||||||
|
Point m_tot = {.x = e.layout.margin.x + e.layout.margin.w, .y = e.layout.margin.y + e.layout.margin.h};
|
||||||
|
Point b_tot = {.x = e.layout.border.x + e.layout.border.w, .y = e.layout.border.y + e.layout.border.h};
|
||||||
|
Point p_tot = {.x = e.layout.padding.x + e.layout.padding.w, .y = e.layout.padding.y + e.layout.padding.h};
|
||||||
|
|
||||||
Rect parent_bounds, parent_view;
|
// ASSIGN WIDTH
|
||||||
Rect child_placement, child_occupied;
|
switch {
|
||||||
|
case e.layout.w.@is_exact():
|
||||||
|
// if width is exact then assign it to the bounds and add border and paddign
|
||||||
|
e.bounds.w = e.layout.w.min + b_tot.x + p_tot.x;
|
||||||
|
case e.layout.w.@is_grow():
|
||||||
|
// done in another pass
|
||||||
|
break;
|
||||||
|
case e.layout.w.@is_fit(): // fit the element's children
|
||||||
|
Size elem_width = e.layout.w; // the element's content width
|
||||||
|
Size children_width = e.layout.children.w; // element children (content) width
|
||||||
|
Size combined = elem_width.combine(children_width);
|
||||||
|
|
||||||
// 1. Select the right origin
|
if (combined.min <= combined.max) { // OK!
|
||||||
Point origin;
|
// the final element width is the content width plus padding and border
|
||||||
switch (div.layout) {
|
e.bounds.w = min(elem_width.max, children_width.max) + b_tot.x + p_tot.x;
|
||||||
case LAYOUT_ROW:
|
|
||||||
origin = div.origin_r;
|
|
||||||
case LAYOUT_COLUMN:
|
|
||||||
origin = div.origin_c;
|
|
||||||
case LAYOUT_FLOATING: // none, relative to zero zero
|
|
||||||
case LAYOUT_ABSOLUTE: // absolute position, this is a no-op, return the rect
|
|
||||||
return rect;
|
|
||||||
default: // error
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Compute the parent's view
|
|
||||||
parent_bounds = parent.bounds;
|
|
||||||
parent_view = parent.get_view();
|
|
||||||
|
|
||||||
// 3. Compute the placement and occupied area
|
|
||||||
|
|
||||||
// grow rect (wanted size) when widht or height are less than zero
|
|
||||||
bool adapt_x = rect.w <= 0;
|
|
||||||
bool adapt_y = rect.h <= 0;
|
|
||||||
if (adapt_x) rect.w = parent_bounds.w - parent_bounds.x - origin.x;
|
|
||||||
if (adapt_y) rect.h = parent_bounds.h - parent_bounds.y - origin.y;
|
|
||||||
|
|
||||||
// offset placement and area
|
|
||||||
child_placement = child_placement.off(origin.add(rect.position()));
|
|
||||||
child_occupied = child_occupied.off(origin.add(rect.position()));
|
|
||||||
|
|
||||||
Rect margin = style.margin;
|
|
||||||
Rect border = style.border;
|
|
||||||
Rect padding = style.padding;
|
|
||||||
|
|
||||||
// padding, grows both the placement and occupied area
|
|
||||||
child_placement = child_placement.grow(padding.position().add(padding.size()));
|
|
||||||
child_occupied = child_occupied.grow(padding.position().add(padding.size()));
|
|
||||||
// border, grows both the placement and occupied area
|
|
||||||
child_placement = child_placement.grow(border.position().add(border.size()));
|
|
||||||
child_occupied = child_occupied.grow(border.position().add(border.size()));
|
|
||||||
// margin, offsets the placement and grows the occupied area
|
|
||||||
child_placement = child_placement.off(margin.position());
|
|
||||||
child_occupied = child_occupied.grow(margin.position().add(margin.size()));
|
|
||||||
|
|
||||||
// oh yeah also adjust the rect if i was to grow
|
|
||||||
if (adapt_x) rect.w -= padding.x+padding.w + border.x+border.w + margin.x+margin.w;
|
|
||||||
if (adapt_y) rect.h -= padding.y+padding.h + border.y+border.h + margin.y+margin.h;
|
|
||||||
|
|
||||||
// set the size
|
|
||||||
child_placement = child_placement.grow(rect.size());
|
|
||||||
child_occupied = child_occupied.grow(rect.size());
|
|
||||||
|
|
||||||
// 4. Update the parent's origin
|
|
||||||
div.origin_r = {
|
|
||||||
.x = child_occupied.bottom_right().x,
|
|
||||||
.y = origin.y,
|
|
||||||
};
|
|
||||||
div.origin_c = {
|
|
||||||
.x = origin.x,
|
|
||||||
.y = child_occupied.bottom_right().y,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 5. Update the parent's children bounds
|
|
||||||
div.children_bounds = containing_rect(div.children_bounds, child_occupied);
|
|
||||||
|
|
||||||
// 99. return the placement
|
|
||||||
if (child_placement.collides(parent_view)) {
|
|
||||||
return child_placement.off(parent.get_view_off().neg());
|
|
||||||
} else {
|
} else {
|
||||||
return {};
|
unreachable("Cannot fit children width: min:%d max:%d", combined.min, combined.max);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Size w = e.needed_width().combine(e.layout.children.w);
|
||||||
|
if (w.max >= w.min) { // OK!
|
||||||
|
e.bounds.w = w.min;
|
||||||
|
} else {
|
||||||
|
println(e.needed_width(), " ", e.needed_height());
|
||||||
|
println(e.layout.children.w, " ", e.layout.children.h);
|
||||||
|
unreachable("Cannot fit children width: min:%d max:%d", w.min, w.max);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
default: unreachable("width is not exact, grow or fit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ASSIGN HEIGHT
|
||||||
|
switch {
|
||||||
|
case e.layout.h.@is_exact():
|
||||||
|
// if width is exact then assign it to the bounds and add border and paddign
|
||||||
|
e.bounds.h = e.layout.h.min + b_tot.y + p_tot.y;
|
||||||
|
case e.layout.h.@is_grow():
|
||||||
|
break;
|
||||||
|
// done in another pass
|
||||||
|
case e.layout.h.@is_fit(): // fit the element's children
|
||||||
|
Size elem_height = e.layout.h; // the element's content width
|
||||||
|
Size children_height = e.layout.children.h; // element children (content) width
|
||||||
|
Size combined = elem_height.combine(children_height);
|
||||||
|
|
||||||
|
if (combined.min <= combined.max) { // OK!
|
||||||
|
// the final element width is the content width plus padding and border
|
||||||
|
e.bounds.h = min(elem_height.max, children_height.max) + b_tot.y + p_tot.y;
|
||||||
|
} else {
|
||||||
|
unreachable("Cannot fit children height: min:%d max:%d", combined.min, combined.max);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Size h = e.needed_height().combine(e.layout.children.h);
|
||||||
|
if (h.max >= h.min) { // OK!
|
||||||
|
e.bounds.h = h.max;
|
||||||
|
} else {
|
||||||
|
unreachable("Cannot fit children height: min:%d max:%d", h.min, h.max);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
default: unreachable("width is not exact, grow or fit");
|
||||||
|
}
|
||||||
|
|
||||||
|
// the occupied space must account for the element's margin as well
|
||||||
|
switch (p.layout.dir) {
|
||||||
|
case ROW:
|
||||||
|
if (!e.layout.w.@is_grow()) p.layout.occupied += e.bounds.w + m_tot.x;
|
||||||
|
case COLUMN:
|
||||||
|
if (!e.layout.h.@is_grow()) p.layout.occupied += e.bounds.h + m_tot.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void resolve_grow_elements(Elem* e, Elem* p)
|
||||||
|
{
|
||||||
|
Point m_tot = {.x = e.layout.margin.x + e.layout.margin.w, .y = e.layout.margin.y + e.layout.margin.h};
|
||||||
|
Point b_tot = {.x = e.layout.border.x + e.layout.border.w, .y = e.layout.border.y + e.layout.border.h};
|
||||||
|
Point p_tot = {.x = e.layout.padding.x + e.layout.padding.w, .y = e.layout.padding.y + e.layout.padding.h};
|
||||||
|
|
||||||
|
// WIDTH
|
||||||
|
if (e.layout.w.@is_grow()) {
|
||||||
|
if (p.layout.dir == ROW) { // grow along the axis, divide the parent size
|
||||||
|
short slot = (short)((p.content_space().x - p.layout.occupied) / p.layout.grow_children);
|
||||||
|
// the space slot accounts for the total size, while the element bounds does not account
|
||||||
|
// for the margin
|
||||||
|
e.bounds.w = slot - m_tot.x;
|
||||||
|
p.layout.grow_children--;
|
||||||
|
p.layout.occupied += slot;
|
||||||
|
} else if (p.layout.dir == COLUMN) { // grow across the layout axis, inherit width of the parent
|
||||||
|
e.bounds.w = p.content_space().x - m_tot.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HEIGHT
|
||||||
|
if (e.layout.h.@is_grow()) {
|
||||||
|
if (p.layout.dir == COLUMN) { // grow along the axis, divide the parent size
|
||||||
|
short slot = (short)((p.content_space().y - p.layout.occupied) / p.layout.grow_children);
|
||||||
|
e.bounds.h = slot - m_tot.y;
|
||||||
|
p.layout.grow_children--;
|
||||||
|
p.layout.occupied += slot;
|
||||||
|
} else if (p.layout.dir == ROW) { // grow across the layout axis, inherit width of the parent
|
||||||
|
e.bounds.h = p.content_space().y - m_tot.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void resolve_placement(Elem* c, Elem* p)
|
||||||
|
{
|
||||||
|
Layout* pl = &p.layout;
|
||||||
|
Layout* cl = &c.layout;
|
||||||
|
|
||||||
|
Point off = {
|
||||||
|
.x = p.bounds.x + pl.origin.x + cl.margin.x,
|
||||||
|
.y = p.bounds.y + pl.origin.y + cl.margin.x,
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (pl.anchor) {
|
||||||
|
case TOP_LEFT:
|
||||||
|
c.bounds.x = off.x;
|
||||||
|
c.bounds.y = off.y;
|
||||||
|
case LEFT:
|
||||||
|
c.bounds.x = off.x;
|
||||||
|
c.bounds.y = off.y + p.bounds.h/2;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.y -= pl.occupied/2;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.y -= c.bounds.h/2;
|
||||||
|
}
|
||||||
|
case BOTTOM_LEFT:
|
||||||
|
c.bounds.x = off.x;
|
||||||
|
c.bounds.y = off.y + p.bounds.h ;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.y -= pl.occupied;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.y -= c.bounds.h;
|
||||||
|
}
|
||||||
|
case BOTTOM:
|
||||||
|
c.bounds.x = off.x + p.bounds.w/2;
|
||||||
|
c.bounds.y = off.y + p.bounds.h;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.y -= pl.occupied;
|
||||||
|
c.bounds.x -= c.bounds.w/2;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.y -= c.bounds.h;
|
||||||
|
c.bounds.x -= pl.occupied/2;
|
||||||
|
}
|
||||||
|
case BOTTOM_RIGHT:
|
||||||
|
c.bounds.x = off.x + p.bounds.w;
|
||||||
|
c.bounds.y = off.y + p.bounds.h;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.y -= pl.occupied;
|
||||||
|
c.bounds.x -= c.bounds.w;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.y -= c.bounds.h;
|
||||||
|
c.bounds.x -= pl.occupied;
|
||||||
|
}
|
||||||
|
case RIGHT:
|
||||||
|
c.bounds.x = off.x + p.bounds.w;
|
||||||
|
c.bounds.y = off.y + p.bounds.h/2;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.y -= pl.occupied/2;
|
||||||
|
c.bounds.x -= c.bounds.w;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.y -= c.bounds.h/2;
|
||||||
|
c.bounds.x -= pl.occupied;
|
||||||
|
}
|
||||||
|
case TOP_RIGHT:
|
||||||
|
c.bounds.x = off.x + p.bounds.w;
|
||||||
|
c.bounds.y = off.y;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.x -= c.bounds.w;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.x -= pl.occupied;
|
||||||
|
}
|
||||||
|
case TOP:
|
||||||
|
c.bounds.x = off.x + p.bounds.w/2;
|
||||||
|
c.bounds.y = off.y;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.x -= c.bounds.w/2;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.x -= pl.occupied/2;
|
||||||
|
}
|
||||||
|
case CENTER:
|
||||||
|
c.bounds.x = off.x + p.bounds.w/2;
|
||||||
|
c.bounds.y = off.y + p.bounds.h/2;
|
||||||
|
if (pl.dir == COLUMN) {
|
||||||
|
c.bounds.x -= c.bounds.w/2;
|
||||||
|
c.bounds.y -= pl.occupied/2;
|
||||||
|
} else if (pl.dir == ROW) {
|
||||||
|
c.bounds.x -= pl.occupied/2;
|
||||||
|
c.bounds.y -= c.bounds.h/2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (pl.dir) {
|
||||||
|
case ROW:
|
||||||
|
pl.origin.x += c.bounds.w + cl.margin.x + cl.margin.w;
|
||||||
|
case COLUMN:
|
||||||
|
pl.origin.y += c.bounds.h + cl.margin.x + cl.margin.w;
|
||||||
|
default: unreachable("unknown layout direction");
|
||||||
|
}
|
||||||
|
|
||||||
|
//sprintln("origin: ", p.layout.origin, " bounds: ", c.bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn void Ctx.layout_element_tree(&ctx)
|
||||||
|
{
|
||||||
|
isz cursor = -1;
|
||||||
|
isz current = ctx.tree.level_order_it(0, &cursor)!!;
|
||||||
|
for (; current >= 0; current = ctx.tree.level_order_it(0, &cursor)!!) {
|
||||||
|
Elem* p = ctx.find_elem(ctx.tree.get(current))!!;
|
||||||
|
// offset the origin by the margin
|
||||||
|
p.layout.origin.x = p.layout.border.x + p.layout.padding.x;
|
||||||
|
p.layout.origin.y = p.layout.border.y + p.layout.padding.y;
|
||||||
|
|
||||||
|
// RESOLVE KNOWN DIMENSIONS
|
||||||
|
isz ch_cur = 0;
|
||||||
|
isz ch = ctx.tree.children_it(current, &ch_cur)!!;
|
||||||
|
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
||||||
|
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
||||||
|
if (ctx.tree.is_root(ch)!!) {
|
||||||
|
resolve_dimensions(p, &&{});
|
||||||
|
} else {
|
||||||
|
resolve_dimensions(c, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESOLVE GROW CHILDREN
|
||||||
|
ch_cur = 0;
|
||||||
|
ch = ctx.tree.children_it(current, &ch_cur)!!;
|
||||||
|
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
||||||
|
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
||||||
|
if (ctx.tree.is_root(ch)!!) {
|
||||||
|
resolve_grow_elements(p, &&{});
|
||||||
|
} else {
|
||||||
|
resolve_grow_elements(c, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RESOLVE CHILDREN PLACEMENT
|
||||||
|
ch_cur = 0;
|
||||||
|
ch = ctx.tree.children_it(current, &ch_cur)!!;
|
||||||
|
for (; ch >= 0; ch = ctx.tree.children_it(current, &ch_cur)!!) {
|
||||||
|
Elem* c = ctx.find_elem(ctx.tree.get(ch))!!;
|
||||||
|
if (ctx.tree.is_root(ch)!!) {
|
||||||
|
resolve_placement(p, &&{});
|
||||||
|
update_children_bounds(c, p);
|
||||||
|
} else {
|
||||||
|
resolve_placement(c, p);
|
||||||
|
update_children_bounds(c, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,6 +174,16 @@ macro Rect Rect.pad(Rect a, Rect b)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro Rect Rect.expand(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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
// POINT //
|
// POINT //
|
||||||
@ -189,39 +199,12 @@ 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 Point Point.add(Point a, Point b)
|
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};
|
||||||
return {
|
macro Point Point.neg(Point p) @operator_s(-) => {-p.x, -p.y};
|
||||||
.x = a.x + b.x,
|
|
||||||
.y = a.y + b.y,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Point Point.sub(Point a, Point b)
|
macro Point Point.max(Point a, Point b) => {.x = max(a.x, b.x), .y = max(a.y, b.y)};
|
||||||
{
|
macro Point Point.min(Point a, Point b) => {.x = min(a.x, b.x), .y = min(a.y, b.y)};
|
||||||
return {
|
|
||||||
.x = a.x - b.x,
|
|
||||||
.y = a.y - b.y,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Point Point.neg(Point p) => {-p.x, -p.y};
|
|
||||||
|
|
||||||
macro Point Point.max(Point a, Point b)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.x = max(a.x, b.x),
|
|
||||||
.y = max(a.y, b.y),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
macro Point Point.min(Point a, Point b)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
.x = min(a.x, b.x),
|
|
||||||
.y = min(a.y, b.y),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------- //
|
// ---------------------------------------------------------------------------------- //
|
||||||
// COLOR //
|
// COLOR //
|
||||||
@ -252,8 +235,26 @@ macro Color uint.@to_rgba($u)
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro uint Color.to_uint(c)
|
macro uint Color.to_uint(c) => c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
||||||
{
|
|
||||||
uint u = c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
// ---------------------------------------------------------------------------------- //
|
||||||
return u;
|
// SIZE //
|
||||||
|
// ---------------------------------------------------------------------------------- //
|
||||||
|
|
||||||
|
struct Size {
|
||||||
|
short min, max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro Size @grow() => {.min = 0, .max = 0};
|
||||||
|
macro Size @exact(short s) => {.min = s, .max = s};
|
||||||
|
macro Size @fit(short min = 0, short max = short.max/2) => {.min = min, .max = max};
|
||||||
|
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_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.sub(a, Size b) @operator_s(-) => {.min = a.min-b.min, .max = 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_min(a, Size b) => {.min = min(a.min, b.min), .max = min(a.max, b.max)};
|
||||||
|
@ -8,12 +8,12 @@ struct ElemSlider {
|
|||||||
Rect handle;
|
Rect handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* handle
|
/* handle
|
||||||
* +----+-----+---------------------+
|
* +----+-----+---------------------+
|
||||||
* | |#####| |
|
* | |#####| |
|
||||||
* +----+-----+---------------------+
|
* +----+-----+---------------------+
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
macro Ctx.slider_hor(&ctx, Rect size, float* value, float hpercent = 0.25, ...)
|
macro Ctx.slider_hor(&ctx, Rect size, float* value, float hpercent = 0.25, ...)
|
||||||
=> ctx.slider_hor_id(@compute_id($vasplat), size, value, hpercent);
|
=> ctx.slider_hor_id(@compute_id($vasplat), size, value, hpercent);
|
||||||
<*
|
<*
|
||||||
@ -63,6 +63,7 @@ fn ElemEvents? Ctx.slider_hor_id(&ctx, Id id, Rect size, float* value, float hpe
|
|||||||
|
|
||||||
return elem.events;
|
return elem.events;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -77,6 +78,7 @@ fn ElemEvents? Ctx.slider_hor_id(&ctx, Id id, Rect size, float* value, float hpe
|
|||||||
* | |
|
* | |
|
||||||
* +--+
|
* +--+
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
macro Ctx.slider_ver(&ctx, Rect size, float* value, float hpercent = 0.25, ...)
|
macro Ctx.slider_ver(&ctx, Rect size, float* value, float hpercent = 0.25, ...)
|
||||||
=> ctx.slider_ver_id(@compute_id($vasplat), size, value, hpercent);
|
=> ctx.slider_ver_id(@compute_id($vasplat), size, value, hpercent);
|
||||||
fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Rect size, float* value, float hpercent = 0.25)
|
fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Rect size, float* value, float hpercent = 0.25)
|
||||||
@ -134,3 +136,4 @@ fn ElemEvents? Ctx.slider_ver_id(&ctx, Id id, Rect size, float* value, float hpe
|
|||||||
macro short calc_slider(short off, short dim, float value) => (short)off + (short)(dim * value);
|
macro short calc_slider(short off, short dim, float value) => (short)off + (short)(dim * value);
|
||||||
macro float calc_value(short off, short mouse, short dim, short slider)
|
macro float calc_value(short off, short mouse, short dim, short slider)
|
||||||
=> math::clamp((float)(mouse-off-slider/2)/(float)(dim-slider), 0.0f, 1.0f);
|
=> math::clamp((float)(mouse-off-slider/2)/(float)(dim-slider), 0.0f, 1.0f);
|
||||||
|
*/
|
@ -44,7 +44,7 @@ fn void? SpriteAtlas.init(&this, String name, AtlasType type, ushort width, usho
|
|||||||
|
|
||||||
this.id = name.hash();
|
this.id = name.hash();
|
||||||
this.atlas.new(this.id, AtlasType.ATLAS_R8G8B8A8, width, height)!;
|
this.atlas.new(this.id, AtlasType.ATLAS_R8G8B8A8, width, height)!;
|
||||||
this.sprites.init(allocator::heap(), capacity: SRITES_PER_ATLAS);
|
this.sprites.init(allocator::mem, capacity: SRITES_PER_ATLAS);
|
||||||
this.should_update = false;
|
this.should_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,13 +104,14 @@ fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
|
|||||||
{
|
{
|
||||||
QOIDesc desc;
|
QOIDesc desc;
|
||||||
|
|
||||||
char[] pixels = qoi::read(allocator::heap(), path, &desc, QOIChannels.RGBA)!;
|
char[] pixels = qoi::read(allocator::mem, path, &desc, QOIChannels.RGBA)!;
|
||||||
defer mem::free(pixels);
|
defer mem::free(pixels);
|
||||||
|
|
||||||
ctx.sprite_atlas.insert(name, type, pixels, (ushort)desc.width, (ushort)desc.height, (ushort)desc.width)!;
|
ctx.sprite_atlas.insert(name, type, pixels, (ushort)desc.width, (ushort)desc.height, (ushort)desc.width)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
macro Ctx.sprite(&ctx, String name, Point off = {0,0}, ...)
|
macro Ctx.sprite(&ctx, String name, Point off = {0,0}, ...)
|
||||||
=> ctx.sprite_id(@compute_id($vasplat), name, off);
|
=> ctx.sprite_id(@compute_id($vasplat), name, off);
|
||||||
fn void? Ctx.sprite_id(&ctx, Id id, String name, Point off)
|
fn void? Ctx.sprite_id(&ctx, Id id, String name, Point off)
|
||||||
@ -151,3 +152,4 @@ fn void? Ctx.draw_sprite_raw(&ctx, String name, Rect bounds, bool center = false
|
|||||||
|
|
||||||
return ctx.push_sprite(bounds, sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!;
|
return ctx.push_sprite(bounds, sprite.uv(), tex_id, parent.div.z_index, type: sprite.type)!;
|
||||||
}
|
}
|
||||||
|
*/
|
@ -9,6 +9,7 @@ struct ElemText {
|
|||||||
Rect bounds;
|
Rect bounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
macro Ctx.text_unbounded(&ctx, String text, ...)
|
macro Ctx.text_unbounded(&ctx, String text, ...)
|
||||||
=> ctx.text_unbounded_id(@compute_id($vasplat), text);
|
=> ctx.text_unbounded_id(@compute_id($vasplat), text);
|
||||||
fn void? Ctx.text_unbounded_id(&ctx, Id id, String text)
|
fn void? Ctx.text_unbounded_id(&ctx, Id id, String text)
|
||||||
@ -77,3 +78,4 @@ fn ElemEvents? Ctx.text_box_id(&ctx, Id id, Rect size, char[] text, usz* text_le
|
|||||||
|
|
||||||
return elem.events;
|
return elem.events;
|
||||||
}
|
}
|
||||||
|
*/
|
@ -4,7 +4,9 @@ default {
|
|||||||
primary: #cc241dff;
|
primary: #cc241dff;
|
||||||
secondary: #458588ff;
|
secondary: #458588ff;
|
||||||
accent: #fabd2fff;
|
accent: #fabd2fff;
|
||||||
border: 1;
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
30
src/main.c3
30
src/main.c3
@ -216,6 +216,7 @@ $endswitch
|
|||||||
TimeStats dts = draw_times.get_stats();
|
TimeStats dts = draw_times.get_stats();
|
||||||
TimeStats uts = ui_times.get_stats();
|
TimeStats uts = ui_times.get_stats();
|
||||||
|
|
||||||
|
/*
|
||||||
ui.layout_set_floating()!!;
|
ui.layout_set_floating()!!;
|
||||||
// FIXME: I cannot anchor shit to the bottom of the screen
|
// FIXME: I cannot anchor shit to the bottom of the screen
|
||||||
ui.@div({0, ui.height-150, -300, 150}) {
|
ui.@div({0, ui.height-150, -300, 150}) {
|
||||||
@ -224,13 +225,13 @@ $endswitch
|
|||||||
ui.text_unbounded(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
|
ui.text_unbounded(string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
|
||||||
ui.text_unbounded(string::tformat("%s %s", mod.lctrl, (String)ui.input.keyboard.text[:ui.input.keyboard.text_len]))!!;
|
ui.text_unbounded(string::tformat("%s %s", mod.lctrl, (String)ui.input.keyboard.text[:ui.input.keyboard.text_len]))!!;
|
||||||
}!!;
|
}!!;
|
||||||
|
*/
|
||||||
|
|
||||||
ui.frame_end()!!;
|
ui.frame_end()!!;
|
||||||
/* End UI Handling */
|
/* End UI Handling */
|
||||||
ui_times.push(clock.mark());
|
ui_times.push(clock.mark());
|
||||||
//ui_times.print_stats();
|
//ui_times.print_stats();
|
||||||
|
|
||||||
|
|
||||||
/* Start UI Drawing */
|
/* Start UI Drawing */
|
||||||
ren.begin_render(true);
|
ren.begin_render(true);
|
||||||
|
|
||||||
@ -253,6 +254,7 @@ $endswitch
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
fn void debug_app(ugui::Ctx* ui)
|
fn void debug_app(ugui::Ctx* ui)
|
||||||
{
|
{
|
||||||
static bool toggle;
|
static bool toggle;
|
||||||
@ -324,6 +326,7 @@ fn void debug_app(ugui::Ctx* ui)
|
|||||||
};
|
};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
import std::os::process;
|
import std::os::process;
|
||||||
|
|
||||||
@ -361,34 +364,36 @@ fn void calculator(ugui::Ctx* ui)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ui input/output
|
// ui input/output
|
||||||
ui.@div(ugui::DIV_FILL) {
|
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) {
|
||||||
ui.layout_set_column()!!;
|
|
||||||
|
|
||||||
|
/*
|
||||||
ui.@div({0,0,250,50}) {
|
ui.@div({0,0,250,50}) {
|
||||||
ui.text_unbounded((String)buffer[:len])!!;
|
ui.text_unbounded((String)buffer[:len])!!;
|
||||||
}!!;
|
}!!;
|
||||||
|
*/
|
||||||
|
|
||||||
ui.@div({0,0,250,-100}) {
|
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
|
||||||
ui.layout_set_row()!!;
|
|
||||||
|
|
||||||
ui.button("7")!!.mouse_press ? buffer[len++] = '7' : 0;
|
ui.button("7")!!.mouse_press ? buffer[len++] = '7' : 0;
|
||||||
ui.button("8")!!.mouse_press ? buffer[len++] = '8' : 0;
|
ui.button("8")!!.mouse_press ? buffer[len++] = '8' : 0;
|
||||||
ui.button("9")!!.mouse_press ? buffer[len++] = '9' : 0;
|
ui.button("9")!!.mouse_press ? buffer[len++] = '9' : 0;
|
||||||
ui.layout_next_row()!!;
|
}!!;
|
||||||
|
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
|
||||||
ui.button("4")!!.mouse_press ? buffer[len++] = '4' : 0;
|
ui.button("4")!!.mouse_press ? buffer[len++] = '4' : 0;
|
||||||
ui.button("5")!!.mouse_press ? buffer[len++] = '5' : 0;
|
ui.button("5")!!.mouse_press ? buffer[len++] = '5' : 0;
|
||||||
ui.button("6")!!.mouse_press ? buffer[len++] = '6' : 0;
|
ui.button("6")!!.mouse_press ? buffer[len++] = '6' : 0;
|
||||||
ui.layout_next_row()!!;
|
}!!;
|
||||||
|
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
|
||||||
ui.button("3")!!.mouse_press ? buffer[len++] = '3' : 0;
|
ui.button("3")!!.mouse_press ? buffer[len++] = '3' : 0;
|
||||||
ui.button("2")!!.mouse_press ? buffer[len++] = '2' : 0;
|
ui.button("2")!!.mouse_press ? buffer[len++] = '2' : 0;
|
||||||
ui.button("1")!!.mouse_press ? buffer[len++] = '1' : 0;
|
ui.button("1")!!.mouse_press ? buffer[len++] = '1' : 0;
|
||||||
ui.layout_next_row()!!;
|
}!!;
|
||||||
|
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
|
||||||
ui.button("0")!!.mouse_press ? buffer[len++] = '0' : 0;
|
ui.button("0")!!.mouse_press ? buffer[len++] = '0' : 0;
|
||||||
ui.button(".")!!.mouse_press ? buffer[len++] = '.' : 0;
|
ui.button(".")!!.mouse_press ? buffer[len++] = '.' : 0;
|
||||||
ui.button("(")!!.mouse_press ? buffer[len++] = '(' : 0;
|
ui.button("(")!!.mouse_press ? buffer[len++] = '(' : 0;
|
||||||
|
}!!;
|
||||||
|
|
||||||
ui.layout_next_column()!!;
|
/*
|
||||||
|
|
||||||
ui.@div({0,0,0,-1}) {
|
ui.@div({0,0,0,-1}) {
|
||||||
ui.button("C")!!.mouse_press ? len = 0 : 0;
|
ui.button("C")!!.mouse_press ? len = 0 : 0;
|
||||||
ui.button("D")!!.mouse_press ? len-- : 0;
|
ui.button("D")!!.mouse_press ? len-- : 0;
|
||||||
@ -409,7 +414,6 @@ fn void calculator(ugui::Ctx* ui)
|
|||||||
buffer[:x.len] = x[..];
|
buffer[:x.len] = x[..];
|
||||||
len = x.len;
|
len = x.len;
|
||||||
}
|
}
|
||||||
}!!;
|
*/
|
||||||
}!!;
|
|
||||||
}!!;
|
}!!;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user