working example of the new layout system
This commit is contained in:
parent
2619873ca7
commit
0f7d5a6506
@ -218,3 +218,22 @@ at frame end:
|
||||
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){};
|
||||
|
||||
Rect min_size = {0, 0, style.size, style.size};
|
||||
Rect text_size = ctx.get_text_bounds(label)!;
|
||||
// TODO: get min size by style
|
||||
TextSize text_size = ctx.measure_string(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 min_size = style.size;
|
||||
ushort half_lh = (ushort)(ctx.font.line_height() / 2);
|
||||
ushort left_pad = label != "" ? half_lh : 0;
|
||||
ushort inner_pad = label != "" && icon != "" ? half_lh : 0;
|
||||
ushort right_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
|
||||
*/
|
||||
|
||||
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.layout.w = {
|
||||
.min = (short)max(min_size, left_pad + icon_size.w + inner_pad + text_size.width.min + right_pad),
|
||||
.max = (short)max(min_size, left_pad + icon_size.w + inner_pad + text_size.width.max + right_pad),
|
||||
};
|
||||
|
||||
elem.bounds = ctx.layout_element(parent, tot_size, style);
|
||||
if (elem.bounds.is_null()) return {};
|
||||
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.events = ctx.get_elem_events(elem);
|
||||
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,
|
||||
.h = content_bounds.h
|
||||
};
|
||||
text_bounds = text_size.center_to(text_bounds);
|
||||
//text_bounds = text_size.center_to(text_bounds);
|
||||
|
||||
Rect icon_bounds = {
|
||||
.x = content_bounds.x,
|
||||
@ -82,6 +95,7 @@ fn ElemEvents? Ctx.button_id(&ctx, Id id, String label, String icon)
|
||||
return elem.events;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// FIXME: this should be inside the style
|
||||
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 = {};
|
||||
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);
|
||||
}
|
||||
if (cull_rect(rect, ctx.div_scissor)) {
|
||||
io::print("NOPE: ");
|
||||
io::print(cmd.rect.rect);
|
||||
io::printn(cmd.z_index);
|
||||
// io::print("NOPE: ");
|
||||
// io::print(cmd.rect.rect);
|
||||
// io::printn(cmd.z_index);
|
||||
// unreachable();
|
||||
return;
|
||||
}
|
||||
|
@ -9,6 +9,15 @@ import std::core::string;
|
||||
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
|
||||
alias Id = uint;
|
||||
|
||||
@ -45,7 +54,9 @@ struct Elem {
|
||||
ElemFlags flags;
|
||||
ElemEvents events;
|
||||
Rect bounds;
|
||||
Rect children_bounds;
|
||||
ElemType type;
|
||||
Layout layout;
|
||||
union {
|
||||
ElemDiv div;
|
||||
ElemButton button;
|
||||
@ -131,6 +142,7 @@ const uint GOLDEN_RATIO = 0x9E3779B9;
|
||||
// with the Cantor pairing function
|
||||
fn Id? Ctx.gen_id(&ctx, Id id2)
|
||||
{
|
||||
// FIXME: this is SHIT
|
||||
Id id1 = ctx.tree.get(ctx.active_div)!;
|
||||
// Mix the two IDs non-linearly
|
||||
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.is_new = is_new;
|
||||
elem.id = id;
|
||||
elem.layout = {};
|
||||
if (is_new == false && elem.type != type) {
|
||||
return WRONG_ELEMENT_TYPE?;
|
||||
} else {
|
||||
@ -232,16 +245,15 @@ fn void? Ctx.frame_begin(&ctx)
|
||||
//elem.flags.has_focus = ctx.has_focus;
|
||||
|
||||
elem.bounds = {0, 0, ctx.width, ctx.height};
|
||||
elem.div.layout = LAYOUT_ROW;
|
||||
elem.div.z_index = 0;
|
||||
elem.div.children_bounds = elem.bounds;
|
||||
elem.div.scroll_x.enabled = false;
|
||||
elem.div.scroll_y.enabled = false;
|
||||
elem.div.pcb = {};
|
||||
elem.div.origin_c = {};
|
||||
elem.div.origin_r = {};
|
||||
elem.layout.dir = ROW;
|
||||
elem.layout.anchor = TOP_LEFT;
|
||||
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
|
||||
// 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
|
||||
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
|
||||
ctx.tree.nuke();
|
||||
|
@ -5,7 +5,6 @@ import std::math;
|
||||
|
||||
// div element
|
||||
struct ElemDiv {
|
||||
Layout layout;
|
||||
struct scroll_x {
|
||||
bool enabled;
|
||||
bool on;
|
||||
@ -18,28 +17,42 @@ struct ElemDiv {
|
||||
}
|
||||
ushort scroll_size;
|
||||
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
|
||||
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();
|
||||
ctx.div_end()!;
|
||||
}
|
||||
|
||||
// begin a widget container, or div, the size determines the offset (x,y) width and height.
|
||||
// if the width or height are zero the width or height are set to the maximum available.
|
||||
// if the width or height are negative the width or height will be calculated based on the children size
|
||||
// sort similar to a flexbox, and the minimum size is set by the negative of the width or height
|
||||
// FIXME: there is a bug if the size.w or size.h == -0
|
||||
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)
|
||||
macro Ctx.div_begin(&ctx,
|
||||
Size width = @grow(), Size height = @grow(),
|
||||
LayoutDirection dir = ROW,
|
||||
Anchor anchor = TOP_LEFT,
|
||||
bool scroll_x = false, bool scroll_y = false,
|
||||
...
|
||||
)
|
||||
{
|
||||
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)!;
|
||||
|
||||
@ -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.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
|
||||
ctx.div_scissor = elem.bounds;
|
||||
ctx.push_scissor(elem.bounds, elem.div.z_index)!;
|
||||
|
||||
// 4. Fill the div fields
|
||||
elem.div.origin_c = {
|
||||
.x = elem.bounds.x,
|
||||
.y = elem.bounds.y
|
||||
// update layout with correct info
|
||||
elem.layout = {
|
||||
.w = width,
|
||||
.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;
|
||||
|
||||
// update parent grow children
|
||||
update_parent_grow(elem, parent);
|
||||
|
||||
// Add the background to the draw stack
|
||||
bool do_border = parent.div.layout == LAYOUT_FLOATING;
|
||||
ctx.push_rect(elem.bounds, elem.div.z_index, style)!;
|
||||
|
||||
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)
|
||||
{
|
||||
// swap the children bounds
|
||||
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;
|
||||
// children bounds bottom-right corner
|
||||
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))!;
|
||||
elem.div.layout = prev_l;
|
||||
}
|
||||
*/
|
||||
|
||||
// the active_div returns to the parent of the current one
|
||||
ctx.active_div = ctx.tree.parentof(ctx.active_div)!;
|
||||
Elem* parent = ctx.get_parent()!;
|
||||
// TODO: reset the scissor back to the parent div
|
||||
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;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// CODEPOINT //
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
// unicode code point, different type for a different hash
|
||||
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();
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// FONT ATLAS //
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
|
||||
/* width and height of a glyph contain the kering advance
|
||||
* (u,v)
|
||||
* +-------------*---+ -
|
||||
@ -60,7 +83,7 @@ struct Font {
|
||||
|
||||
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.size = height*scale;
|
||||
@ -160,26 +183,39 @@ fn void Font.free(&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)
|
||||
{
|
||||
return ctx.font.load(name, path, height, scale);
|
||||
}
|
||||
|
||||
<*
|
||||
@require off != null
|
||||
@require str.ptr != null
|
||||
*>
|
||||
fn Codepoint str_to_codepoint(char[] str, usz* off)
|
||||
// TODO: check if the font is present in the context
|
||||
fn Id Ctx.get_font_id(&ctx, String label)
|
||||
{
|
||||
Codepoint cp;
|
||||
isz b = grapheme::decode_utf8(str, str.len, (uint*)&cp);
|
||||
if (b == 0 || b > str.len) {
|
||||
return 0;
|
||||
}
|
||||
*off = b;
|
||||
return cp;
|
||||
return (Id)label.hash();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// TODO: change the name
|
||||
@ -189,6 +225,7 @@ struct TextInfo {
|
||||
Rect bounds;
|
||||
String text;
|
||||
usz off;
|
||||
Size width, height;
|
||||
|
||||
// current glyph info
|
||||
Point origin;
|
||||
@ -295,20 +332,81 @@ fn Rect? Ctx.get_text_bounds(&ctx, String text)
|
||||
return ti.text_bounds;
|
||||
}
|
||||
|
||||
// TODO: check if the font is present in the context
|
||||
fn Id Ctx.get_font_id(&ctx, String label)
|
||||
{
|
||||
return (Id)label.hash();
|
||||
|
||||
struct TextSize {
|
||||
Size width, height;
|
||||
}
|
||||
|
||||
fn Atlas*? Ctx.get_font_atlas(&ctx, String name)
|
||||
// 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)
|
||||
{
|
||||
// TODO: use the font name, for now there is only one font
|
||||
if (name.hash() != ctx.font.id) {
|
||||
return WRONG_ID?;
|
||||
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;
|
||||
}
|
||||
|
||||
// update the word width
|
||||
switch {
|
||||
case ascii::is_space((char)cp):
|
||||
if (word_width > ts.width.min) ts.width.min = word_width;
|
||||
word_width = 0;
|
||||
words++;
|
||||
default:
|
||||
word_width += gp.w + gp.ox;
|
||||
if (off < text.len) word_width += gp.adv;
|
||||
}
|
||||
}
|
||||
// end of string is also end of word
|
||||
if (word_width > ts.width.min) ts.width.min = word_width;
|
||||
|
||||
return &ctx.font.atlas;
|
||||
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;
|
||||
}
|
||||
|
||||
fn int Font.line_height(&font) => (int)(font.ascender - font.descender + (float)0.5);
|
||||
|
@ -1,54 +1,102 @@
|
||||
module ugui;
|
||||
|
||||
enum Layout {
|
||||
LAYOUT_ROW,
|
||||
LAYOUT_COLUMN,
|
||||
LAYOUT_FLOATING,
|
||||
LAYOUT_ABSOLUTE,
|
||||
import std::math;
|
||||
import std::io;
|
||||
|
||||
enum LayoutDirection {
|
||||
ROW,
|
||||
COLUMN,
|
||||
}
|
||||
|
||||
fn void? Ctx.layout_set_row(&ctx)
|
||||
{
|
||||
Elem *parent = ctx.get_parent()!;
|
||||
parent.div.layout = LAYOUT_ROW;
|
||||
enum Anchor {
|
||||
TOP_LEFT,
|
||||
LEFT,
|
||||
BOTTOM_LEFT,
|
||||
BOTTOM,
|
||||
BOTTOM_RIGHT,
|
||||
RIGHT,
|
||||
TOP_RIGHT,
|
||||
TOP,
|
||||
CENTER
|
||||
}
|
||||
|
||||
fn void? Ctx.layout_set_column(&ctx)
|
||||
{
|
||||
Elem *parent = ctx.get_parent()!;
|
||||
parent.div.layout = LAYOUT_COLUMN;
|
||||
struct Layout {
|
||||
Size w, h;
|
||||
struct children {
|
||||
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()!;
|
||||
parent.div.layout = LAYOUT_FLOATING;
|
||||
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.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 = {
|
||||
.x = parent.bounds.x,
|
||||
.y = parent.div.children_bounds.bottom_right().y,
|
||||
// The content space of the element
|
||||
macro Point Elem.content_space(&e)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Update the parent element's grow children counter
|
||||
fn void update_parent_grow(Elem* child, Elem* 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++;
|
||||
}
|
||||
}
|
||||
|
||||
fn void? Ctx.layout_next_column(&ctx)
|
||||
// Update the parent element's children size
|
||||
fn void update_parent_size(Elem* child, Elem* parent)
|
||||
{
|
||||
Elem *parent = ctx.get_parent()!;
|
||||
Layout* cl = &child.layout;
|
||||
Layout* pl = &parent.layout;
|
||||
|
||||
parent.div.origin_c = {
|
||||
.x = parent.div.children_bounds.bottom_right().x,
|
||||
.y = parent.bounds.y,
|
||||
};
|
||||
parent.div.origin_r = parent.div.origin_c;
|
||||
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.get_view(&elem)
|
||||
{
|
||||
Rect off;
|
||||
@ -67,91 +115,266 @@ macro Point Elem.get_view_off(&elem)
|
||||
{
|
||||
return elem.get_view().sub(elem.bounds).position();
|
||||
}
|
||||
*/
|
||||
|
||||
// position the rectangle inside the parent according to the layout
|
||||
// parent: parent div
|
||||
// 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)
|
||||
// Assign the width and height of an element in the directions that it doesn't need to grow
|
||||
fn void resolve_dimensions(Elem* e, Elem* p)
|
||||
{
|
||||
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;
|
||||
Rect child_placement, child_occupied;
|
||||
// ASSIGN WIDTH
|
||||
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
|
||||
Point origin;
|
||||
switch (div.layout) {
|
||||
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 {};
|
||||
if (combined.min <= combined.max) { // OK!
|
||||
// the final element width is the content width plus padding and border
|
||||
e.bounds.w = min(elem_width.max, children_width.max) + b_tot.x + p_tot.x;
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
|
||||
// 2. Compute the parent's view
|
||||
parent_bounds = parent.bounds;
|
||||
parent_view = parent.get_view();
|
||||
// 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);
|
||||
|
||||
// 3. Compute the placement and occupied area
|
||||
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");
|
||||
}
|
||||
|
||||
// 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 {
|
||||
return {};
|
||||
// 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 //
|
||||
@ -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);
|
||||
}
|
||||
|
||||
macro Point Point.add(Point a, Point b)
|
||||
{
|
||||
return {
|
||||
.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.neg(Point p) @operator_s(-) => {-p.x, -p.y};
|
||||
|
||||
macro Point Point.sub(Point a, Point b)
|
||||
{
|
||||
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),
|
||||
};
|
||||
}
|
||||
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)};
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// COLOR //
|
||||
@ -252,8 +235,26 @@ macro Color uint.@to_rgba($u)
|
||||
};
|
||||
}
|
||||
|
||||
macro uint Color.to_uint(c)
|
||||
{
|
||||
uint u = c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
||||
return u;
|
||||
macro uint Color.to_uint(c) => c.r | (c.g << 8) | (c.b << 16) | (c.a << 24);
|
||||
|
||||
// ---------------------------------------------------------------------------------- //
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
/* handle
|
||||
* +----+-----+---------------------+
|
||||
* | |#####| |
|
||||
* +----+-----+---------------------+
|
||||
*/
|
||||
/*
|
||||
macro Ctx.slider_hor(&ctx, Rect size, float* value, float hpercent = 0.25, ...)
|
||||
=> 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;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
@ -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, ...)
|
||||
=> 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)
|
||||
@ -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 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);
|
||||
*/
|
@ -44,7 +44,7 @@ fn void? SpriteAtlas.init(&this, String name, AtlasType type, ushort width, usho
|
||||
|
||||
this.id = name.hash();
|
||||
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;
|
||||
}
|
||||
|
||||
@ -104,13 +104,14 @@ fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
|
||||
{
|
||||
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);
|
||||
|
||||
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}, ...)
|
||||
=> ctx.sprite_id(@compute_id($vasplat), name, 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)!;
|
||||
}
|
||||
*/
|
@ -9,6 +9,7 @@ struct ElemText {
|
||||
Rect bounds;
|
||||
}
|
||||
|
||||
/*
|
||||
macro Ctx.text_unbounded(&ctx, String text, ...)
|
||||
=> ctx.text_unbounded_id(@compute_id($vasplat), 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;
|
||||
}
|
||||
*/
|
@ -4,7 +4,9 @@ default {
|
||||
primary: #cc241dff;
|
||||
secondary: #458588ff;
|
||||
accent: #fabd2fff;
|
||||
border: 1;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button {
|
||||
|
32
src/main.c3
32
src/main.c3
@ -216,6 +216,7 @@ $endswitch
|
||||
TimeStats dts = draw_times.get_stats();
|
||||
TimeStats uts = ui_times.get_stats();
|
||||
|
||||
/*
|
||||
ui.layout_set_floating()!!;
|
||||
// FIXME: I cannot anchor shit to the bottom of the screen
|
||||
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("%s %s", mod.lctrl, (String)ui.input.keyboard.text[:ui.input.keyboard.text_len]))!!;
|
||||
}!!;
|
||||
*/
|
||||
|
||||
ui.frame_end()!!;
|
||||
/* End UI Handling */
|
||||
ui_times.push(clock.mark());
|
||||
//ui_times.print_stats();
|
||||
|
||||
|
||||
/* Start UI Drawing */
|
||||
ren.begin_render(true);
|
||||
|
||||
@ -253,6 +254,7 @@ $endswitch
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
fn void debug_app(ugui::Ctx* ui)
|
||||
{
|
||||
static bool toggle;
|
||||
@ -324,6 +326,7 @@ fn void debug_app(ugui::Ctx* ui)
|
||||
};
|
||||
ui.div_end()!!;
|
||||
}
|
||||
*/
|
||||
|
||||
import std::os::process;
|
||||
|
||||
@ -361,34 +364,36 @@ fn void calculator(ugui::Ctx* ui)
|
||||
}
|
||||
|
||||
// ui input/output
|
||||
ui.@div(ugui::DIV_FILL) {
|
||||
ui.layout_set_column()!!;
|
||||
ui.@div(ugui::@grow(), ugui::@grow(), ROW, CENTER) {
|
||||
|
||||
/*
|
||||
ui.@div({0,0,250,50}) {
|
||||
ui.text_unbounded((String)buffer[:len])!!;
|
||||
}!!;
|
||||
*/
|
||||
|
||||
ui.@div({0,0,250,-100}) {
|
||||
ui.layout_set_row()!!;
|
||||
|
||||
ui.@div(ugui::@fit(), ugui::@fit(), COLUMN) {
|
||||
ui.button("7")!!.mouse_press ? buffer[len++] = '7' : 0;
|
||||
ui.button("8")!!.mouse_press ? buffer[len++] = '8' : 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("5")!!.mouse_press ? buffer[len++] = '5' : 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("2")!!.mouse_press ? buffer[len++] = '2' : 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(".")!!.mouse_press ? buffer[len++] = '.' : 0;
|
||||
ui.button("(")!!.mouse_press ? buffer[len++] = '(' : 0;
|
||||
|
||||
ui.layout_next_column()!!;
|
||||
|
||||
}!!;
|
||||
|
||||
/*
|
||||
ui.@div({0,0,0,-1}) {
|
||||
ui.button("C")!!.mouse_press ? len = 0 : 0;
|
||||
ui.button("D")!!.mouse_press ? len-- : 0;
|
||||
@ -409,7 +414,6 @@ fn void calculator(ugui::Ctx* ui)
|
||||
buffer[:x.len] = x[..];
|
||||
len = x.len;
|
||||
}
|
||||
}!!;
|
||||
}!!;
|
||||
*/
|
||||
}!!;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user