update project to c3 0.7.1
This commit is contained in:
parent
34e75f8c06
commit
79a2d66880
34
TODO
34
TODO
@ -13,6 +13,8 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[x] Clip element bounds to parent div, specifically text
|
[x] Clip element bounds to parent div, specifically text
|
||||||
[ ] Resizeable divs
|
[ ] Resizeable divs
|
||||||
[ ] Implement a z index and sort command buffer based on that
|
[ ] Implement a z index and sort command buffer based on that
|
||||||
|
[ ] Ctx.set_z_index()
|
||||||
|
[ ] Sort command buffer on insertion
|
||||||
[ ] Standardize element handling, for example all buttons do almost the same thing, so write a lot of boiler plate and reuse it
|
[ ] Standardize element handling, for example all buttons do almost the same thing, so write a lot of boiler plate and reuse it
|
||||||
[x] The id combination in gen_id() uses an intger division, which is costly, use another combination function that is non-linear and doesn't use division
|
[x] The id combination in gen_id() uses an intger division, which is costly, use another combination function that is non-linear and doesn't use division
|
||||||
[ ] Animations, somehow
|
[ ] Animations, somehow
|
||||||
@ -33,12 +35,19 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
[ ] Subdivide modules into ugui::ug for exported functions and ugui::core for
|
||||||
internal use functions (used to create widgets)
|
internal use functions (used to create widgets)
|
||||||
[ ] The render loop RAPES the gpu, valve pls fix
|
[ ] The render loop RAPES the gpu, valve pls fix
|
||||||
|
[ ] The way the element structures are implemented wastes a lot of memory since
|
||||||
|
each struct Elem, struct Cmd, etc. is as big as the largest element. It would
|
||||||
|
be better to use a different allcation strategy.
|
||||||
|
[ ] Add a way to handle time events like double clicks
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
[ ] Text reflow
|
[ ] Text reflow
|
||||||
[x] Flexbox
|
[x] Flexbox
|
||||||
[ ] Center elements to the row/column
|
[ ] Center elements to the row/column
|
||||||
|
[ ] Text wrapping
|
||||||
|
[ ] Consider a multi-pass recursive approach to layout (like https://github.com/nicbarker/clay)
|
||||||
|
instead of the curren multi-frame approach.
|
||||||
|
|
||||||
## Input
|
## Input
|
||||||
|
|
||||||
@ -51,15 +60,16 @@ to maintain focus until mouse release (fix scroll bars)
|
|||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
[x] rect commads should have:
|
[x] rect commads should have:
|
||||||
_ border width
|
- border width
|
||||||
_ border radius
|
- border radius
|
||||||
[x] add a command to update an atlas
|
[x] add a command to update an atlas
|
||||||
[ ] New window command, useful for popups
|
[ ] New window command, useful for popups
|
||||||
|
|
||||||
## Atlases
|
## Atlas
|
||||||
|
|
||||||
[ ] Add an interface to create, destroy, update and get atlases based on their ids
|
[ ] Add an interface to create, destroy, update and get atlases based on their ids
|
||||||
[ ] Implement multiple font atlases
|
[ ] Implement multiple font atlases
|
||||||
|
[ ] Pixel format conversion
|
||||||
|
|
||||||
## Fonts
|
## Fonts
|
||||||
|
|
||||||
@ -79,3 +89,21 @@ _ border radius
|
|||||||
[ ] Icon Buttons
|
[ ] Icon Buttons
|
||||||
[x] Switch
|
[x] Switch
|
||||||
[x] Checkbox
|
[x] Checkbox
|
||||||
|
[ ] Selectable text box
|
||||||
|
|
||||||
|
## Main / exaple
|
||||||
|
|
||||||
|
[ ] Create maps from ids to textures and images instead of hardcoding them
|
||||||
|
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
[ ] Introduce a Layout structure that specifies the positioning of elements inside
|
||||||
|
a Div element. This would allow specifying alignment, maximum and minimum sizing
|
||||||
|
margins between children, etc.
|
||||||
|
This is different from style which is applied per-element.
|
||||||
|
[ ] Remove Ids for element that don't need them. Such elements are button, toggles,
|
||||||
|
and all elements which do not have internal data that has to be cached and/or
|
||||||
|
queried by the user for later use. This allows for smaller caches and in general
|
||||||
|
reduces some load, since most of the stuff is recomputed for every frame.
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
module schrift;
|
module schrift;
|
||||||
|
|
||||||
def SftFont = void*;
|
alias SftFont = void*;
|
||||||
def SftUChar = uint;
|
alias SftUChar = uint;
|
||||||
def SftGlyph = uint;
|
alias SftGlyph = uint;
|
||||||
|
|
||||||
const int SFT_DOWNWARD_Y = 0x01;
|
const int SFT_DOWNWARD_Y = 0x01;
|
||||||
|
|
||||||
|
31
src/cache.c3
31
src/cache.c3
@ -1,4 +1,4 @@
|
|||||||
module cache(<Key, Value, SIZE>);
|
module cache{Key, Value, SIZE};
|
||||||
|
|
||||||
/* LRU Cache
|
/* LRU Cache
|
||||||
* The cache uses a pool (array) to store all the elements, each element has
|
* The cache uses a pool (array) to store all the elements, each element has
|
||||||
@ -14,12 +14,13 @@ module cache(<Key, Value, SIZE>);
|
|||||||
// happens at the same time
|
// happens at the same time
|
||||||
|
|
||||||
import std::core::mem;
|
import std::core::mem;
|
||||||
|
import std::core::mem::allocator;
|
||||||
import std::collections::bitset;
|
import std::collections::bitset;
|
||||||
import std::collections::map;
|
import std::collections::map;
|
||||||
|
|
||||||
def BitArr = bitset::BitSet(<SIZE>) @private;
|
alias BitArr = bitset::BitSet{SIZE};
|
||||||
def IdTable = map::HashMap(<Key, usz>) @private;
|
alias IdTable = map::HashMap{Key, usz};
|
||||||
def IdTableEntry = map::Entry(<Key, usz>) @private;
|
alias IdTableEntry = map::Entry{Key, usz};
|
||||||
|
|
||||||
const usz CACHE_NCYCLES = (usz)(SIZE * 2.0/3.0);
|
const usz CACHE_NCYCLES = (usz)(SIZE * 2.0/3.0);
|
||||||
|
|
||||||
@ -42,9 +43,9 @@ macro Cache.cycle(&cache) @private {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Cache.init(&cache)
|
fn void? Cache.init(&cache)
|
||||||
{
|
{
|
||||||
cache.table.new_init(capacity: SIZE);
|
cache.table.init(allocator::heap(), capacity: SIZE);
|
||||||
// FIXME: this shit is SLOW
|
// FIXME: this shit is SLOW
|
||||||
foreach (idx, bit : cache.used) { cache.used[idx] = false; }
|
foreach (idx, bit : cache.used) { cache.used[idx] = false; }
|
||||||
foreach (idx, bit : cache.present) { cache.present[idx] = false; }
|
foreach (idx, bit : cache.present) { cache.present[idx] = false; }
|
||||||
@ -57,7 +58,7 @@ fn void Cache.free(&cache)
|
|||||||
(void)mem::free(cache.pool);
|
(void)mem::free(cache.pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value*! Cache.search(&cache, Key id)
|
fn Value*? Cache.search(&cache, Key id)
|
||||||
{
|
{
|
||||||
// get_entry() faults on miss
|
// get_entry() faults on miss
|
||||||
IdTableEntry* entry = cache.table.get_entry(id)!;
|
IdTableEntry* entry = cache.table.get_entry(id)!;
|
||||||
@ -65,14 +66,14 @@ fn Value*! Cache.search(&cache, Key id)
|
|||||||
/* MISS, wrong key */
|
/* MISS, wrong key */
|
||||||
if (entry.key != id) {
|
if (entry.key != id) {
|
||||||
cache.table.remove(id)!;
|
cache.table.remove(id)!;
|
||||||
return SearchResult.MISSING?;
|
return NOT_FOUND?;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MISS, the data is not valid (not present) */
|
/* MISS, the data is not valid (not present) */
|
||||||
if (!cache.present[entry.value]) {
|
if (!cache.present[entry.value]) {
|
||||||
// if the data is not present but it is still in the table, remove it
|
// if the data is not present but it is still in the table, remove it
|
||||||
cache.table.remove(id)!;
|
cache.table.remove(id)!;
|
||||||
return SearchResult.MISSING?;
|
return NOT_FOUND?;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* HIT, set as recently used */
|
/* HIT, set as recently used */
|
||||||
@ -82,7 +83,7 @@ fn Value*! Cache.search(&cache, Key id)
|
|||||||
|
|
||||||
fn void Cache.remove(&cache, Key id)
|
fn void Cache.remove(&cache, Key id)
|
||||||
{
|
{
|
||||||
IdTableEntry*! entry = cache.table.get_entry(id);
|
IdTableEntry*? entry = cache.table.get_entry(id);
|
||||||
if (catch entry) {
|
if (catch entry) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -105,7 +106,7 @@ fn usz Cache.get_free_spot(&cache) @private
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value*! Cache.insert_at(&cache, Value *g, Key id, usz index) @private
|
fn Value*? Cache.insert_at(&cache, Value *g, Key id, usz index) @private
|
||||||
{
|
{
|
||||||
// TODO: verify index, g and id
|
// TODO: verify index, g and id
|
||||||
Value* spot;
|
Value* spot;
|
||||||
@ -122,17 +123,17 @@ fn Value*! Cache.insert_at(&cache, Value *g, Key id, usz index) @private
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert an element in the cache, returns the index
|
// Insert an element in the cache, returns the index
|
||||||
fn Value*! Cache.insert_new(&cache, Value* g, Key id)
|
fn Value*? Cache.insert_new(&cache, Value* g, Key id)
|
||||||
{
|
{
|
||||||
usz index = cache.get_free_spot();
|
usz index = cache.get_free_spot();
|
||||||
return cache.insert_at(g, id, index);
|
return cache.insert_at(g, id, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Value*! Cache.get_or_insert(&cache, Value* g, Key id, bool *is_new = null)
|
fn Value*? Cache.get_or_insert(&cache, Value* g, Key id, bool *is_new = null)
|
||||||
{
|
{
|
||||||
Value*! c = cache.search(id);
|
Value*? c = cache.search(id);
|
||||||
if (catch e = c) {
|
if (catch e = c) {
|
||||||
if (e != SearchResult.MISSING) {
|
if (e != NOT_FOUND) {
|
||||||
return e?;
|
return e?;
|
||||||
} else {
|
} else {
|
||||||
// if the element is new (inserted) set the is_new flag
|
// if the element is new (inserted) set the is_new flag
|
||||||
|
17
src/fifo.c3
17
src/fifo.c3
@ -1,11 +1,8 @@
|
|||||||
module fifo(<Type>);
|
module fifo{Type};
|
||||||
|
|
||||||
import std::core::mem;
|
import std::core::mem;
|
||||||
|
|
||||||
fault FifoErr {
|
faultdef FULL, EMPTY;
|
||||||
FULL,
|
|
||||||
EMPTY,
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: specify the allocator
|
// TODO: specify the allocator
|
||||||
|
|
||||||
@ -15,7 +12,7 @@ struct Fifo {
|
|||||||
usz count;
|
usz count;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Fifo.init(&fifo, usz size)
|
fn void? Fifo.init(&fifo, usz size)
|
||||||
{
|
{
|
||||||
fifo.arr = mem::new_array(Type, size);
|
fifo.arr = mem::new_array(Type, size);
|
||||||
fifo.out = 0;
|
fifo.out = 0;
|
||||||
@ -27,20 +24,20 @@ fn void Fifo.free(&fifo)
|
|||||||
(void)mem::free(fifo.arr);
|
(void)mem::free(fifo.arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Fifo.enqueue(&fifo, Type *elem)
|
fn void? Fifo.enqueue(&fifo, Type *elem)
|
||||||
{
|
{
|
||||||
if (fifo.count >= fifo.arr.len) {
|
if (fifo.count >= fifo.arr.len) {
|
||||||
return FifoErr.FULL?;
|
return FULL?;
|
||||||
}
|
}
|
||||||
usz in = (fifo.out + fifo.count) % fifo.arr.len;
|
usz in = (fifo.out + fifo.count) % fifo.arr.len;
|
||||||
fifo.arr[in] = *elem;
|
fifo.arr[in] = *elem;
|
||||||
fifo.count++;
|
fifo.count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Type*! Fifo.dequeue(&fifo)
|
fn Type*? Fifo.dequeue(&fifo)
|
||||||
{
|
{
|
||||||
if (fifo.count == 0) {
|
if (fifo.count == 0) {
|
||||||
return FifoErr.EMPTY?;
|
return EMPTY?;
|
||||||
}
|
}
|
||||||
Type *ret = &fifo.arr[fifo.out];
|
Type *ret = &fifo.arr[fifo.out];
|
||||||
fifo.count--;
|
fifo.count--;
|
||||||
|
74
src/main.c3
74
src/main.c3
@ -7,7 +7,7 @@ import std::time;
|
|||||||
import std::collections::ringbuffer;
|
import std::collections::ringbuffer;
|
||||||
import std::core::string;
|
import std::core::string;
|
||||||
|
|
||||||
def Times = ringbuffer::RingBuffer(<time::NanoDuration, 128>);
|
alias Times = ringbuffer::RingBuffer{time::NanoDuration[128]};
|
||||||
|
|
||||||
fn void Times.print_stats(×)
|
fn void Times.print_stats(×)
|
||||||
{
|
{
|
||||||
@ -40,7 +40,7 @@ fn TimeStats Times.get_stats(×)
|
|||||||
}
|
}
|
||||||
avg = (NanoDuration)((ulong)avg/128.0);
|
avg = (NanoDuration)((ulong)avg/128.0);
|
||||||
|
|
||||||
return TimeStats{.min = min, .max = max, .avg = avg};
|
return {.min = min, .max = max, .avg = avg};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,12 +115,12 @@ void main()
|
|||||||
|
|
||||||
macro rl::Color ugui::Color.conv(color)
|
macro rl::Color ugui::Color.conv(color)
|
||||||
{
|
{
|
||||||
return rl::Color{.r = color.r, .g = color.g, .b = color.b, .a = color.a};
|
return {.r = color.r, .g = color.g, .b = color.b, .a = color.a};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro rl::Rectangle Rect.conv(rect)
|
macro rl::Rectangle Rect.conv(rect)
|
||||||
{
|
{
|
||||||
return rl::Rectangle{.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h};
|
return {.x = rect.x, .y = rect.y, .width = rect.w, .height = rect.h};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int main(String[] args)
|
fn int main(String[] args)
|
||||||
@ -169,11 +169,13 @@ fn int main(String[] args)
|
|||||||
mod.lctrl = rl::isKeyDown(rl::KEY_LEFT_CONTROL);
|
mod.lctrl = rl::isKeyDown(rl::KEY_LEFT_CONTROL);
|
||||||
ui.input_mod_keys(mod);
|
ui.input_mod_keys(mod);
|
||||||
|
|
||||||
|
/*
|
||||||
for (rl::KeyboardKey key; (key = (KeyboardKey)rl::getKeyPressed()) != 0;) {
|
for (rl::KeyboardKey key; (key = (KeyboardKey)rl::getKeyPressed()) != 0;) {
|
||||||
ZString kname = rl::getKeyName(key);
|
ZString kname = rl::getKeyName(key);
|
||||||
if (kname == null) continue;
|
if (kname == null) continue;
|
||||||
ui.input_text_unicode(kname.str_view());
|
ui.input_text_unicode(kname.str_view());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/* Start Input Handling */
|
/* Start Input Handling */
|
||||||
@ -203,29 +205,29 @@ fn int main(String[] args)
|
|||||||
|
|
||||||
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) break;
|
if (ui.check_key_combo(ugui::KMOD_CTRL, "q")) break;
|
||||||
|
|
||||||
ui.div_begin("main", Rect{.w=-100})!!;
|
ui.div_begin("main", {.w=-100})!!;
|
||||||
{|
|
{
|
||||||
ui.layout_set_column()!!;
|
ui.layout_set_column()!!;
|
||||||
if (ui.button("button0", Rect{0,0,30,30}, toggle)!!.mouse_press) {
|
if (ui.button("button0", {0,0,30,30}, toggle)!!.mouse_press) {
|
||||||
io::printn("press button0");
|
io::printn("press button0");
|
||||||
toggle = !toggle;
|
toggle = !toggle;
|
||||||
}
|
}
|
||||||
//ui.layout_next_column()!!;
|
//ui.layout_next_column()!!;
|
||||||
if (ui.button("button1", Rect{0,0,30,30})!!.mouse_press) {
|
if (ui.button("button1", {0,0,30,30})!!.mouse_press) {
|
||||||
io::printn("press button1");
|
io::printn("press button1");
|
||||||
}
|
}
|
||||||
//ui.layout_next_column()!!;
|
//ui.layout_next_column()!!;
|
||||||
if (ui.button("button2", Rect{0,0,30,30})!!.mouse_release) {
|
if (ui.button("button2", {0,0,30,30})!!.mouse_release) {
|
||||||
io::printn("release button2");
|
io::printn("release button2");
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.layout_set_row()!!;
|
ui.layout_set_row()!!;
|
||||||
ui.layout_next_row()!!;
|
ui.layout_next_row()!!;
|
||||||
static float rf, gf, bf, af;
|
static float rf, gf, bf, af;
|
||||||
ui.slider_ver("slider_r", Rect{0,0,30,100}, &rf)!!;
|
ui.slider_ver("slider_r", {0,0,30,100}, &rf)!!;
|
||||||
ui.slider_ver("slider_g", Rect{0,0,30,100}, &gf)!!;
|
ui.slider_ver("slider_g", {0,0,30,100}, &gf)!!;
|
||||||
ui.slider_ver("slider_b", Rect{0,0,30,100}, &bf)!!;
|
ui.slider_ver("slider_b", {0,0,30,100}, &bf)!!;
|
||||||
ui.slider_ver("slider_a", Rect{0,0,30,100}, &af)!!;
|
ui.slider_ver("slider_a", {0,0,30,100}, &af)!!;
|
||||||
|
|
||||||
ui.layout_next_column()!!;
|
ui.layout_next_column()!!;
|
||||||
ui.text_unbounded("text1", "Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!;
|
ui.text_unbounded("text1", "Ciao Mamma\nAbilità ⚡\n'\udb80\udd2c'")!!;
|
||||||
@ -235,8 +237,8 @@ fn int main(String[] args)
|
|||||||
|
|
||||||
ui.layout_next_row()!!;
|
ui.layout_next_row()!!;
|
||||||
static bool check;
|
static bool check;
|
||||||
ui.checkbox("check1", "", Point{}, &check, "tick")!!;
|
ui.checkbox("check1", "", {}, &check, "tick")!!;
|
||||||
ui.toggle("toggle1", "", Point{}, &toggle)!!;
|
ui.toggle("toggle1", "", {}, &toggle)!!;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
ui.layout_set_column()!!;
|
ui.layout_set_column()!!;
|
||||||
@ -248,33 +250,33 @@ fn int main(String[] args)
|
|||||||
ui.layout_next_row()!!;
|
ui.layout_next_row()!!;
|
||||||
ui.button_label(" E ")!!;
|
ui.button_label(" E ")!!;
|
||||||
*/
|
*/
|
||||||
|};
|
};
|
||||||
ui.draw_sprite("sprite1", "tux")!!;
|
ui.draw_sprite("sprite1", "tux")!!;
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!;
|
ui.div_begin("second", ugui::DIV_FILL, scroll_x: true, scroll_y: true)!!;
|
||||||
{|
|
{
|
||||||
ui.layout_set_column()!!;
|
ui.layout_set_column()!!;
|
||||||
static float slider2 = 0.5;
|
static float slider2 = 0.5;
|
||||||
if (ui.slider_ver("slider", Rect{0,0,30,100}, &slider2)!!.update) {
|
if (ui.slider_ver("slider", {0,0,30,100}, &slider2)!!.update) {
|
||||||
io::printfn("other slider: %f", slider2);
|
io::printfn("other slider: %f", slider2);
|
||||||
}
|
}
|
||||||
ui.button("button0", Rect{0,0,50,50})!!;
|
ui.button("button0", {0,0,50,50})!!;
|
||||||
ui.button("button1", Rect{0,0,50,50})!!;
|
ui.button("button1", {0,0,50,50})!!;
|
||||||
ui.button("button2", Rect{0,0,50,50})!!;
|
ui.button("button2", {0,0,50,50})!!;
|
||||||
ui.button("button3", Rect{0,0,50,50})!!;
|
ui.button("button3", {0,0,50,50})!!;
|
||||||
if (toggle) {
|
if (toggle) {
|
||||||
ui.button("button4", Rect{0,0,50,50})!!;
|
ui.button("button4", {0,0,50,50})!!;
|
||||||
ui.button("button5", Rect{0,0,50,50})!!;
|
ui.button("button5", {0,0,50,50})!!;
|
||||||
ui.button("button6", Rect{0,0,50,50})!!;
|
ui.button("button6", {0,0,50,50})!!;
|
||||||
ui.button("button7", Rect{0,0,50,50})!!;
|
ui.button("button7", {0,0,50,50})!!;
|
||||||
}
|
}
|
||||||
ui.layout_next_column()!!;
|
ui.layout_next_column()!!;
|
||||||
ui.layout_set_row()!!;
|
ui.layout_set_row()!!;
|
||||||
static float f1, f2;
|
static float f1, f2;
|
||||||
ui.slider_hor("hs1", Rect{0,0,100,30}, &f1)!!;
|
ui.slider_hor("hs1", {0,0,100,30}, &f1)!!;
|
||||||
ui.slider_hor("hs2", Rect{0,0,100,30}, &f2)!!;
|
ui.slider_hor("hs2", {0,0,100,30}, &f2)!!;
|
||||||
|};
|
};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
// Timings counter
|
// Timings counter
|
||||||
@ -282,12 +284,12 @@ fn int main(String[] args)
|
|||||||
TimeStats uts = ui_times.get_stats();
|
TimeStats uts = ui_times.get_stats();
|
||||||
|
|
||||||
ui.layout_set_floating()!!;
|
ui.layout_set_floating()!!;
|
||||||
ui.div_begin("fps", Rect{0, ui.height-100, -300, 100})!!;
|
ui.div_begin("fps", {0, ui.height-100, -300, 100})!!;
|
||||||
{|
|
{
|
||||||
ui.layout_set_column()!!;
|
ui.layout_set_column()!!;
|
||||||
ui.text_unbounded("draw times", string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
|
ui.text_unbounded("draw times", string::tformat("ui avg: %s\ndraw avg: %s\nTOT: %s", uts.avg, dts.avg, uts.avg+dts.avg))!!;
|
||||||
ui.text_unbounded("ui text input", (String)ui.input.keyboard.text[..])!!;
|
ui.text_unbounded("ui text input", (String)ui.input.keyboard.text[..])!!;
|
||||||
|};
|
};
|
||||||
ui.div_end()!!;
|
ui.div_end()!!;
|
||||||
|
|
||||||
ui.frame_end()!!;
|
ui.frame_end()!!;
|
||||||
@ -341,15 +343,15 @@ fn int main(String[] args)
|
|||||||
rl::endShaderMode();
|
rl::endShaderMode();
|
||||||
} else if (cmd.sprite.texture_id == sprite_id) {
|
} else if (cmd.sprite.texture_id == sprite_id) {
|
||||||
// FIXME: THIS CODE IS SHIT, REAL DOO DOO
|
// FIXME: THIS CODE IS SHIT, REAL DOO DOO
|
||||||
{|
|
{
|
||||||
if (cmd.sprite.type == SpriteType.SPRITE_MSDF) {
|
if (cmd.sprite.type == SpriteType.SPRITE_MSDF) {
|
||||||
rl::beginShaderMode(msdf_shader);
|
rl::beginShaderMode(msdf_shader);
|
||||||
defer rl::endShaderMode();
|
defer rl::endShaderMode();
|
||||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
||||||
} else {
|
} else {
|
||||||
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), rl::Vector2{0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
rl::drawTexturePro(sprite_texture, cmd.sprite.texture_rect.conv(), cmd.sprite.rect.conv(), {0.0f, 0.0f}, 0.0f, cmd.sprite.hue.conv());
|
||||||
}
|
}
|
||||||
|};
|
};
|
||||||
} else {
|
} else {
|
||||||
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
|
io::printfn("unknown texture id: %d", cmd.sprite.texture_id);
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,7 @@ module ugui;
|
|||||||
|
|
||||||
import std::io;
|
import std::io;
|
||||||
|
|
||||||
fault UgAtlasError {
|
faultdef CANNOT_PLACE, INVALID_TYPE;
|
||||||
CANNOT_PLACE,
|
|
||||||
INVALID_TYPE,
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AtlasType {
|
enum AtlasType {
|
||||||
ATLAS_GRAYSCALE,
|
ATLAS_GRAYSCALE,
|
||||||
@ -33,7 +30,39 @@ macro usz AtlasType.bpp(type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Atlas.new(&atlas, Id id, AtlasType type, ushort width, ushort height)
|
macro typeid AtlasType.underlying(type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case ATLAS_GRAYSCALE: return char;
|
||||||
|
case ATLAS_R8G8B8A8: return uint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// FIXME: in and out types are not always known at compile time
|
||||||
|
macro @pixel_convert(p, AtlasType $in, AtlasType $out)
|
||||||
|
{
|
||||||
|
$if $in == $out:
|
||||||
|
return p;
|
||||||
|
$else
|
||||||
|
$switch
|
||||||
|
$case $in == ATLAS_R8G8B8A8 && $out == ATLAS_GRAYSCALE:
|
||||||
|
var r = ((p >> 0) & 0xff);
|
||||||
|
var g = ((p >> 8) & 0xff);
|
||||||
|
var b = ((p >> 16) & 0xff);
|
||||||
|
var a = ((p >> 24) & 0xff);
|
||||||
|
if (a == 0) return (char)0;
|
||||||
|
return (ATLAS_GRAYSCALE.underlying())(((float)r+g+b) / 3.0f);
|
||||||
|
$case $in == ATLAS_GRAYSCALE && $out == ATLAS_R8G8B8A8:
|
||||||
|
var x = (char)(p/3.0);
|
||||||
|
return (ATLAS_R8G8B8A8.underlying())(x|(x<<8)|(x<<16)|(255<<24));
|
||||||
|
$default: $error "Unimplemented pixel format conversion";
|
||||||
|
$endswitch
|
||||||
|
$endif
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
fn void? Atlas.new(&atlas, Id id, AtlasType type, ushort width, ushort height)
|
||||||
{
|
{
|
||||||
atlas.id = id;
|
atlas.id = id;
|
||||||
atlas.type = type;
|
atlas.type = type;
|
||||||
@ -66,7 +95,7 @@ fn void Atlas.free(&atlas)
|
|||||||
// place a rect inside the atlas
|
// place a rect inside the atlas
|
||||||
// uses a row first algorithm
|
// uses a row first algorithm
|
||||||
// TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/
|
// TODO: use a skyline algorithm https://jvernay.fr/en/blog/skyline-2d-packer/implementation/
|
||||||
fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride)
|
fn Point? Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride)
|
||||||
{
|
{
|
||||||
Point p;
|
Point p;
|
||||||
|
|
||||||
@ -79,7 +108,7 @@ fn Point! Atlas.place(&atlas, char[] pixels, ushort w, ushort h, ushort stride)
|
|||||||
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
|
if (atlas.row.x + w <= atlas.width && atlas.row.y + h <= atlas.height) {
|
||||||
p = atlas.row;
|
p = atlas.row;
|
||||||
} else {
|
} else {
|
||||||
return UgAtlasError.CANNOT_PLACE?;
|
return CANNOT_PLACE?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ struct ElemButton {
|
|||||||
// draw a button, return the events on that button
|
// draw a button, return the events on that button
|
||||||
// FIXME: "state" should be renamed "active" to toggle between an usable button and
|
// FIXME: "state" should be renamed "active" to toggle between an usable button and
|
||||||
// an inactive (greyed-out) button
|
// an inactive (greyed-out) button
|
||||||
fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false)
|
fn ElemEvents? Ctx.button(&ctx, String label, Rect size, bool state = false)
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -22,14 +22,14 @@ fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false)
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_BUTTON;
|
elem.type = ETYPE_BUTTON;
|
||||||
} else if (elem.type != ETYPE_BUTTON) {
|
} else if (elem.type != ETYPE_BUTTON) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
elem.bounds = ctx.position_element(parent, size, true);
|
elem.bounds = ctx.position_element(parent, size, true);
|
||||||
|
|
||||||
// if the bounds are null the element is outside the div view,
|
// if the bounds are null the element is outside the div view,
|
||||||
// no interaction should occur so just return
|
// no interaction should occur so just return
|
||||||
if (elem.bounds.is_null()) { return ElemEvents{}; }
|
if (elem.bounds.is_null()) { return {}; }
|
||||||
|
|
||||||
Color col = 0x0000ffffu.to_rgba();
|
Color col = 0x0000ffffu.to_rgba();
|
||||||
elem.events = ctx.get_elem_events(elem);
|
elem.events = ctx.get_elem_events(elem);
|
||||||
@ -45,7 +45,7 @@ fn ElemEvents! Ctx.button(&ctx, String label, Rect size, bool state = false)
|
|||||||
return elem.events;
|
return elem.events;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.max,short.max}, bool state = false)
|
fn ElemEvents? Ctx.button_label(&ctx, String label, Rect size = {0,0,short.max,short.max}, bool state = false)
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -60,11 +60,11 @@ fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.m
|
|||||||
|
|
||||||
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
|
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
|
||||||
Rect text_size = ctx.get_text_bounds(label)!;
|
Rect text_size = ctx.get_text_bounds(label)!;
|
||||||
Rect btn_size = text_size.add(Rect{0,0,10,10});
|
Rect btn_size = text_size.add({0,0,10,10});
|
||||||
|
|
||||||
// 2. Layout
|
// 2. Layout
|
||||||
elem.bounds = ctx.position_element(parent, btn_size, true);
|
elem.bounds = ctx.position_element(parent, btn_size, true);
|
||||||
if (elem.bounds.is_null()) { return ElemEvents{}; }
|
if (elem.bounds.is_null()) { return {}; }
|
||||||
|
|
||||||
Color col = 0x0000ffffu.to_rgba();
|
Color col = 0x0000ffffu.to_rgba();
|
||||||
elem.events = ctx.get_elem_events(elem);
|
elem.events = ctx.get_elem_events(elem);
|
||||||
@ -86,9 +86,50 @@ fn ElemEvents! Ctx.button_label(&ctx, String label, Rect size = Rect{0,0,short.m
|
|||||||
return elem.events;
|
return elem.events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ElemEvents? Ctx.button_icon(&ctx, String label, String icon, String on_icon = "", bool state = false)
|
||||||
|
{
|
||||||
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
|
Elem *parent = ctx.get_parent()!;
|
||||||
|
Elem *elem = ctx.get_elem(id)!;
|
||||||
|
// add it to the tree
|
||||||
|
ctx.tree.add(id, ctx.active_div)!;
|
||||||
|
|
||||||
|
if (elem.flags.is_new) {
|
||||||
|
elem.type = ETYPE_BUTTON;
|
||||||
|
} else if (elem.type != ETYPE_BUTTON) {
|
||||||
|
return WRONG_ELEMENT_TYPE?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sprite* def_sprite = ctx.sprite_atlas.get(icon)!;
|
||||||
|
Sprite* on_sprite = ctx.sprite_atlas.get(on_icon) ?? &&(Sprite){};
|
||||||
|
Rect max_size = def_sprite.rect().max(on_sprite.rect());
|
||||||
|
|
||||||
|
elem.bounds = ctx.position_element(parent, max_size, true);
|
||||||
|
|
||||||
|
// if the bounds are null the element is outside the div view,
|
||||||
|
// no interaction should occur so just return
|
||||||
|
if (elem.bounds.is_null()) { return {}; }
|
||||||
|
|
||||||
|
Color col = 0x0000ffffu.to_rgba();
|
||||||
|
elem.events = ctx.get_elem_events(elem);
|
||||||
|
|
||||||
|
Id tex_id = ctx.sprite_atlas.id;
|
||||||
|
if (state && on_icon != "") {
|
||||||
|
ctx.push_sprite(elem.bounds, on_sprite.uv(), tex_id, type: on_sprite.type)!;
|
||||||
|
} else {
|
||||||
|
ctx.push_sprite(elem.bounds, def_sprite.uv(), tex_id, type: def_sprite.type)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the button
|
||||||
|
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
||||||
|
|
||||||
|
return elem.events;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: this should be inside the style
|
// FIXME: this should be inside the style
|
||||||
const ushort DEFAULT_CHECKBOX_SIZE = 16;
|
const ushort DEFAULT_CHECKBOX_SIZE = 16;
|
||||||
fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {})
|
fn void? Ctx.checkbox(&ctx, String label, String description, Point off, bool* state, String tick_sprite = {})
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -102,7 +143,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_BUTTON;
|
elem.type = ETYPE_BUTTON;
|
||||||
} else if (elem.type != ETYPE_BUTTON) {
|
} else if (elem.type != ETYPE_BUTTON) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect size = {off.x, off.y, DEFAULT_CHECKBOX_SIZE, DEFAULT_CHECKBOX_SIZE};
|
Rect size = {off.x, off.y, DEFAULT_CHECKBOX_SIZE, DEFAULT_CHECKBOX_SIZE};
|
||||||
@ -116,7 +157,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
|
|||||||
if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state);
|
if (elem.events.mouse_hover && elem.events.mouse_release) *state = !(*state);
|
||||||
|
|
||||||
Color col;
|
Color col;
|
||||||
if (tick_sprite != String{}) {
|
if (tick_sprite != {}) {
|
||||||
col = ctx.style.bgcolor;
|
col = ctx.style.bgcolor;
|
||||||
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
ctx.push_rect(elem.bounds, col, do_border: true, do_radius: true)!;
|
||||||
if (*state) {
|
if (*state) {
|
||||||
@ -135,7 +176,7 @@ fn void! Ctx.checkbox(&ctx, String label, String description, Point off, bool* s
|
|||||||
|
|
||||||
// FIXME: this should be inside the style
|
// FIXME: this should be inside the style
|
||||||
const short DEFAULT_SWITCH_SIZE = 16;
|
const short DEFAULT_SWITCH_SIZE = 16;
|
||||||
fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* state)
|
fn void? Ctx.toggle(&ctx, String label, String description, Point off, bool* state)
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -149,7 +190,7 @@ fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* sta
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_BUTTON;
|
elem.type = ETYPE_BUTTON;
|
||||||
} else if (elem.type != ETYPE_BUTTON) {
|
} else if (elem.type != ETYPE_BUTTON) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect size = {off.x, off.y, DEFAULT_SWITCH_SIZE*2, DEFAULT_SWITCH_SIZE};
|
Rect size = {off.x, off.y, DEFAULT_SWITCH_SIZE*2, DEFAULT_SWITCH_SIZE};
|
||||||
@ -171,6 +212,6 @@ fn void! Ctx.toggle(&ctx, String label, String description, Point off, bool* sta
|
|||||||
// Draw the button
|
// Draw the button
|
||||||
// FIXME: THIS IS SHIT
|
// FIXME: THIS IS SHIT
|
||||||
ctx.push_rect(elem.bounds, ctx.style.bgcolor, do_border: true, do_radius: true)!;
|
ctx.push_rect(elem.bounds, ctx.style.bgcolor, do_border: true, do_radius: true)!;
|
||||||
Rect t = elem.bounds.add(Rect{*state ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6});
|
Rect t = elem.bounds.add({*state ? (DEFAULT_SWITCH_SIZE+3) : +3, +3, -DEFAULT_SWITCH_SIZE-6, -6});
|
||||||
ctx.push_rect(t, col, do_border: false, do_radius: true)!;
|
ctx.push_rect(t, col, do_border: false, do_radius: true)!;
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ macro Ctx.push_cmd(&ctx, Cmd *cmd)
|
|||||||
|
|
||||||
// FIXME: is this really the best solution?
|
// FIXME: is this really the best solution?
|
||||||
// "rect" is the bounding box of the element, which includes the border and the padding (so not just the content)
|
// "rect" is the bounding box of the element, which includes the border and the padding (so not just the content)
|
||||||
fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false)
|
fn void? Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, bool do_padding = false, bool do_radius = false)
|
||||||
{
|
{
|
||||||
Rect border = ctx.style.border;
|
Rect border = ctx.style.border;
|
||||||
Rect padding = ctx.style.padding;
|
Rect padding = ctx.style.padding;
|
||||||
@ -101,7 +101,7 @@ fn void! Ctx.push_rect(&ctx, Rect rect, Color color, bool do_border = false, boo
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add texture id
|
// TODO: add texture id
|
||||||
fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL)
|
fn void? Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color hue = 0xffffffffu.to_rgba(), SpriteType type = SPRITE_NORMAL)
|
||||||
{
|
{
|
||||||
Cmd cmd = {
|
Cmd cmd = {
|
||||||
.type = CMD_SPRITE,
|
.type = CMD_SPRITE,
|
||||||
@ -114,7 +114,7 @@ fn void! Ctx.push_sprite(&ctx, Rect bounds, Rect texture, Id texture_id, Color h
|
|||||||
ctx.push_cmd(&cmd)!;
|
ctx.push_cmd(&cmd)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu.to_rgba())
|
fn void? Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu.to_rgba())
|
||||||
{
|
{
|
||||||
if (text.len == 0) {
|
if (text.len == 0) {
|
||||||
return;
|
return;
|
||||||
@ -163,10 +163,10 @@ fn void! Ctx.push_string(&ctx, Rect bounds, String text, Color hue = 0xffffffffu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: we never get here if an error was thrown before
|
// FIXME: we never get here if an error was thrown before
|
||||||
ctx.push_scissor(Rect{})!;
|
ctx.push_scissor({})!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.push_update_atlas(&ctx, Atlas* atlas)
|
fn void? Ctx.push_update_atlas(&ctx, Atlas* atlas)
|
||||||
{
|
{
|
||||||
Cmd up = {
|
Cmd up = {
|
||||||
.type = CMD_UPDATE_ATLAS,
|
.type = CMD_UPDATE_ATLAS,
|
||||||
@ -181,7 +181,7 @@ fn void! Ctx.push_update_atlas(&ctx, Atlas* atlas)
|
|||||||
ctx.push_cmd(&up)!;
|
ctx.push_cmd(&up)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.push_scissor(&ctx, Rect rect)
|
fn void? Ctx.push_scissor(&ctx, Rect rect)
|
||||||
{
|
{
|
||||||
Cmd sc = {
|
Cmd sc = {
|
||||||
.type = CMD_SCISSOR,
|
.type = CMD_SCISSOR,
|
||||||
|
@ -9,7 +9,7 @@ import std::core::string;
|
|||||||
|
|
||||||
|
|
||||||
// element ids are just long ints
|
// element ids are just long ints
|
||||||
def Id = usz;
|
alias Id = usz;
|
||||||
|
|
||||||
enum ElemType {
|
enum ElemType {
|
||||||
ETYPE_NONE,
|
ETYPE_NONE,
|
||||||
@ -54,20 +54,15 @@ struct Elem {
|
|||||||
|
|
||||||
|
|
||||||
// relationships between elements are stored in a tree, it stores just the ids
|
// relationships between elements are stored in a tree, it stores just the ids
|
||||||
def IdTree = vtree::VTree(<Id>) @private;
|
alias IdTree = vtree::VTree{Id};
|
||||||
|
|
||||||
// elements themselves are kept in a cache
|
// elements themselves are kept in a cache
|
||||||
const uint MAX_ELEMENTS = 256;
|
const uint MAX_ELEMENTS = 256;
|
||||||
def ElemCache = cache::Cache(<Id, Elem, MAX_ELEMENTS>) @private;
|
alias ElemCache = cache::Cache{Id, Elem, MAX_ELEMENTS};
|
||||||
|
|
||||||
def CmdQueue = fifo::Fifo(<Cmd>);
|
alias CmdQueue = fifo::Fifo{Cmd};
|
||||||
|
|
||||||
fault UgError {
|
faultdef INVALID_SIZE, EVENT_UNSUPPORTED, UNEXPECTED_ELEMENT, WRONG_ELEMENT_TYPE;
|
||||||
INVALID_SIZE,
|
|
||||||
EVENT_UNSUPPORTED,
|
|
||||||
UNEXPECTED_ELEMENT,
|
|
||||||
WRONG_ELEMENT_TYPE,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 };
|
const Rect DIV_FILL = { .x = 0, .y = 0, .w = 0, .h = 0 };
|
||||||
|
|
||||||
@ -128,7 +123,7 @@ struct Ctx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// return a pointer to the parent of the current active div
|
// return a pointer to the parent of the current active div
|
||||||
fn Elem*! Ctx.get_parent(&ctx)
|
fn Elem*? Ctx.get_parent(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
return ctx.cache.search(parent_id);
|
return ctx.cache.search(parent_id);
|
||||||
@ -140,7 +135,7 @@ const uint GOLDEN_RATIO = 0x9E3779B9;
|
|||||||
|
|
||||||
// generate an id combining the hashes of the parent id and the label
|
// generate an id combining the hashes of the parent id and the label
|
||||||
// with the Cantor pairing function
|
// with the Cantor pairing function
|
||||||
macro Id! Ctx.gen_id(&ctx, String label)
|
macro Id? Ctx.gen_id(&ctx, String label)
|
||||||
{
|
{
|
||||||
Id id1 = ctx.tree.get(ctx.active_div)!;
|
Id id1 = ctx.tree.get(ctx.active_div)!;
|
||||||
Id id2 = label.hash();
|
Id id2 = label.hash();
|
||||||
@ -153,7 +148,7 @@ macro Id! Ctx.gen_id(&ctx, String label)
|
|||||||
|
|
||||||
// get or push an element from the cache, return a pointer to it
|
// get or push an element from the cache, return a pointer to it
|
||||||
// resets all flags except is_new which is set accordingly
|
// resets all flags except is_new which is set accordingly
|
||||||
fn Elem*! Ctx.get_elem(&ctx, Id id)
|
fn Elem*? Ctx.get_elem(&ctx, Id id)
|
||||||
{
|
{
|
||||||
Elem empty_elem;
|
Elem empty_elem;
|
||||||
bool is_new;
|
bool is_new;
|
||||||
@ -170,10 +165,10 @@ fn Elem*! Ctx.get_elem(&ctx, Id id)
|
|||||||
// THIS HAS TO BE A MACRO SINCE IT RETURNS A POINTER TO A TEMPORARY VALUE
|
// THIS HAS TO BE A MACRO SINCE IT RETURNS A POINTER TO A TEMPORARY VALUE
|
||||||
macro Elem* Ctx.find_elem(&ctx, Id id)
|
macro Elem* Ctx.find_elem(&ctx, Id id)
|
||||||
{
|
{
|
||||||
Elem*! elem;
|
Elem*? elem;
|
||||||
elem = ctx.cache.search(id);
|
elem = ctx.cache.search(id);
|
||||||
if (catch elem) {
|
if (catch elem) {
|
||||||
return &&Elem{};
|
return &&(Elem){};
|
||||||
}
|
}
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
@ -194,7 +189,7 @@ macro Ctx.get_elem_by_tree_idx(&ctx, isz idx) @private
|
|||||||
return ctx.cache.search(id);
|
return ctx.cache.search(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.init(&ctx)
|
fn void? Ctx.init(&ctx)
|
||||||
{
|
{
|
||||||
ctx.tree.init(MAX_ELEMENTS)!;
|
ctx.tree.init(MAX_ELEMENTS)!;
|
||||||
defer catch { (void)ctx.tree.free(); }
|
defer catch { (void)ctx.tree.free(); }
|
||||||
@ -208,9 +203,9 @@ fn void! Ctx.init(&ctx)
|
|||||||
ctx.active_div = 0;
|
ctx.active_div = 0;
|
||||||
|
|
||||||
// TODO: add style config
|
// TODO: add style config
|
||||||
ctx.style.margin = Rect{2, 2, 2, 2};
|
ctx.style.margin = {2, 2, 2, 2};
|
||||||
ctx.style.border = Rect{2, 2, 2, 2};
|
ctx.style.border = {2, 2, 2, 2};
|
||||||
ctx.style.padding = Rect{1, 1, 1, 1};
|
ctx.style.padding = {1, 1, 1, 1};
|
||||||
ctx.style.radius = 5;
|
ctx.style.radius = 5;
|
||||||
ctx.style.bgcolor = 0x282828ffu.to_rgba();
|
ctx.style.bgcolor = 0x282828ffu.to_rgba();
|
||||||
ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba();
|
ctx.style.fgcolor = 0xfbf1c7ffu.to_rgba();
|
||||||
@ -226,7 +221,7 @@ fn void Ctx.free(&ctx)
|
|||||||
(void)ctx.sprite_atlas.free();
|
(void)ctx.sprite_atlas.free();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.frame_begin(&ctx)
|
fn void? Ctx.frame_begin(&ctx)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 2. Get the root element from the cache and update it
|
// 2. Get the root element from the cache and update it
|
||||||
@ -268,7 +263,7 @@ fn void! Ctx.frame_begin(&ctx)
|
|||||||
// TODO: add a background color taken from a theme or config
|
// TODO: add a background color taken from a theme or config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.frame_end(&ctx)
|
fn void? Ctx.frame_end(&ctx)
|
||||||
{
|
{
|
||||||
Elem* root = ctx.get_elem_by_tree_idx(0)!;
|
Elem* root = ctx.get_elem_by_tree_idx(0)!;
|
||||||
root.div.layout = LAYOUT_ROW;
|
root.div.layout = LAYOUT_ROW;
|
||||||
|
@ -28,7 +28,7 @@ struct ElemDiv {
|
|||||||
// if the width or height are negative the width or height will be calculated based on the children size
|
// 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
|
// 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
|
// FIXME: there is a bug if the size.w or size.h == -0
|
||||||
fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, bool scroll_y = false)
|
fn void? Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, bool scroll_y = false)
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_DIV;
|
elem.type = ETYPE_DIV;
|
||||||
} else if (elem.type != ETYPE_DIV) {
|
} else if (elem.type != ETYPE_DIV) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
elem.div.scroll_x.enabled = scroll_x;
|
elem.div.scroll_x.enabled = scroll_x;
|
||||||
elem.div.scroll_y.enabled = scroll_y;
|
elem.div.scroll_y.enabled = scroll_y;
|
||||||
@ -55,16 +55,16 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo
|
|||||||
.h = size.h < 0 ? max(elem.div.pcb.h, (short)-size.h) : size.h,
|
.h = size.h < 0 ? max(elem.div.pcb.h, (short)-size.h) : size.h,
|
||||||
};
|
};
|
||||||
elem.bounds = ctx.position_element(parent, wanted_size);
|
elem.bounds = ctx.position_element(parent, wanted_size);
|
||||||
elem.div.children_bounds = Rect{};
|
elem.div.children_bounds = {};
|
||||||
|
|
||||||
// update the ctx scissor
|
// update the ctx scissor
|
||||||
ctx.div_scissor = elem.bounds;
|
ctx.div_scissor = elem.bounds;
|
||||||
ctx.push_scissor(elem.bounds)!;
|
ctx.push_scissor(elem.bounds)!;
|
||||||
|
|
||||||
// 4. Fill the div fields
|
// 4. Fill the div fields
|
||||||
elem.div.origin_c = Point{
|
elem.div.origin_c = {
|
||||||
.x = elem.bounds.x,
|
.x = elem.bounds.x,
|
||||||
.y = elem.bounds.y,
|
.y = elem.bounds.y
|
||||||
};
|
};
|
||||||
elem.div.origin_r = elem.div.origin_c;
|
elem.div.origin_r = elem.div.origin_c;
|
||||||
elem.div.layout = parent.div.layout;
|
elem.div.layout = parent.div.layout;
|
||||||
@ -79,7 +79,7 @@ fn void! Ctx.div_begin(&ctx, String label, Rect size, bool scroll_x = false, boo
|
|||||||
// TODO: check resizeable
|
// TODO: check resizeable
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.div_end(&ctx)
|
fn void? Ctx.div_end(&ctx)
|
||||||
{
|
{
|
||||||
// swap the children bounds
|
// swap the children bounds
|
||||||
Elem* parent = ctx.get_parent()!;
|
Elem* parent = ctx.get_parent()!;
|
||||||
|
@ -4,13 +4,15 @@ import schrift;
|
|||||||
import grapheme;
|
import grapheme;
|
||||||
import std::collections::map;
|
import std::collections::map;
|
||||||
import std::core::mem;
|
import std::core::mem;
|
||||||
|
import std::core::mem::allocator;
|
||||||
import std::io;
|
import std::io;
|
||||||
import std::ascii;
|
import std::ascii;
|
||||||
|
|
||||||
|
|
||||||
// unicode code point, different type for a different hash
|
// unicode code point, different type for a different hash
|
||||||
def Codepoint = uint;
|
alias Codepoint = uint;
|
||||||
|
|
||||||
|
//macro uint Codepoint.hash(self) => ((uint)self).hash();
|
||||||
|
|
||||||
/* width and height of a glyph contain the kering advance
|
/* width and height of a glyph contain the kering advance
|
||||||
* (u,v)
|
* (u,v)
|
||||||
@ -40,14 +42,9 @@ struct Glyph {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint FONT_CACHED = 255;
|
const uint FONT_CACHED = 255;
|
||||||
def GlyphTable = map::HashMap(<Codepoint, Glyph>) @private;
|
alias GlyphTable = map::HashMap{Codepoint, Glyph};
|
||||||
|
|
||||||
fault UgFontError {
|
faultdef TTF_LOAD_FAILED, MISSING_GLYPH, BAD_GLYPH_METRICS, RENDER_ERROR;
|
||||||
TTF_LOAD_FAILED,
|
|
||||||
MISSING_GLYPH,
|
|
||||||
BAD_GLYPH_METRICS,
|
|
||||||
RENDER_ERROR,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Font {
|
struct Font {
|
||||||
schrift::Sft sft;
|
schrift::Sft sft;
|
||||||
@ -61,14 +58,14 @@ struct Font {
|
|||||||
bool should_update; // should send update_atlas command, resets at frame_end()
|
bool should_update; // should send update_atlas command, resets at frame_end()
|
||||||
}
|
}
|
||||||
|
|
||||||
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.new_init(capacity: FONT_CACHED);
|
font.table.init(allocator::heap(), capacity: FONT_CACHED);
|
||||||
font.id = name.hash();
|
font.id = name.hash();
|
||||||
|
|
||||||
font.size = height*scale;
|
font.size = height*scale;
|
||||||
|
|
||||||
font.sft = schrift::Sft{
|
font.sft = {
|
||||||
.xScale = (double)font.size,
|
.xScale = (double)font.size,
|
||||||
.yScale = (double)font.size,
|
.yScale = (double)font.size,
|
||||||
.flags = schrift::SFT_DOWNWARD_Y,
|
.flags = schrift::SFT_DOWNWARD_Y,
|
||||||
@ -77,7 +74,7 @@ fn void! Font.load(&font, String name, ZString path, uint height, float scale)
|
|||||||
font.sft.font = schrift::loadfile(path);
|
font.sft.font = schrift::loadfile(path);
|
||||||
if (font.sft.font == null) {
|
if (font.sft.font == null) {
|
||||||
font.table.free();
|
font.table.free();
|
||||||
return UgFontError.TTF_LOAD_FAILED?;
|
return TTF_LOAD_FAILED?;
|
||||||
}
|
}
|
||||||
|
|
||||||
schrift::SftLMetrics lmetrics;
|
schrift::SftLMetrics lmetrics;
|
||||||
@ -98,13 +95,13 @@ fn void! Font.load(&font, String name, ZString path, uint height, float scale)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Glyph*! Font.get_glyph(&font, Codepoint code)
|
fn Glyph*? Font.get_glyph(&font, Codepoint code)
|
||||||
{
|
{
|
||||||
Glyph*! gp;
|
Glyph*? gp;
|
||||||
gp = font.table.get_ref(code);
|
gp = font.table.get_ref(code);
|
||||||
|
|
||||||
if (catch excuse = gp) {
|
if (catch excuse = gp) {
|
||||||
if (excuse != SearchResult.MISSING) {
|
if (excuse != NOT_FOUND) {
|
||||||
return excuse?;
|
return excuse?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -117,12 +114,12 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code)
|
|||||||
schrift::SftGlyph gid;
|
schrift::SftGlyph gid;
|
||||||
schrift::SftGMetrics gmtx;
|
schrift::SftGMetrics gmtx;
|
||||||
|
|
||||||
if (schrift::lookup(&font.sft, code, &gid) < 0) {
|
if (schrift::lookup(&font.sft, (SftUChar)code, &gid) < 0) {
|
||||||
return UgFontError.MISSING_GLYPH?;
|
return MISSING_GLYPH?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schrift::gmetrics(&font.sft, gid, &gmtx) < 0) {
|
if (schrift::gmetrics(&font.sft, gid, &gmtx) < 0) {
|
||||||
return UgFontError.BAD_GLYPH_METRICS?;
|
return BAD_GLYPH_METRICS?;
|
||||||
}
|
}
|
||||||
|
|
||||||
schrift::SftImage img = {
|
schrift::SftImage img = {
|
||||||
@ -132,7 +129,7 @@ fn Glyph*! Font.get_glyph(&font, Codepoint code)
|
|||||||
char[] pixels = mem::new_array(char, (usz)img.width * img.height);
|
char[] pixels = mem::new_array(char, (usz)img.width * img.height);
|
||||||
img.pixels = pixels;
|
img.pixels = pixels;
|
||||||
if (schrift::render(&font.sft, gid, img) < 0) {
|
if (schrift::render(&font.sft, gid, img) < 0) {
|
||||||
return UgFontError.RENDER_ERROR?;
|
return RENDER_ERROR?;
|
||||||
}
|
}
|
||||||
|
|
||||||
glyph.code = code;
|
glyph.code = code;
|
||||||
@ -163,7 +160,7 @@ fn void Font.free(&font)
|
|||||||
schrift::freefont(font.sft.font);
|
schrift::freefont(font.sft.font);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
@ -174,7 +171,7 @@ fn void! Ctx.load_font(&ctx, String name, ZString path, uint height, float scale
|
|||||||
fn Codepoint str_to_codepoint(char[] str, usz* off)
|
fn Codepoint str_to_codepoint(char[] str, usz* off)
|
||||||
{
|
{
|
||||||
Codepoint cp;
|
Codepoint cp;
|
||||||
isz b = grapheme::decode_utf8(str, str.len, &cp);
|
isz b = grapheme::decode_utf8(str, str.len, (uint*)&cp);
|
||||||
if (b == 0 || b > str.len) {
|
if (b == 0 || b > str.len) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -182,7 +179,7 @@ fn Codepoint str_to_codepoint(char[] str, usz* off)
|
|||||||
return cp;
|
return cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Rect! Ctx.get_text_bounds(&ctx, String text)
|
fn Rect? Ctx.get_text_bounds(&ctx, String text)
|
||||||
{
|
{
|
||||||
Rect text_bounds;
|
Rect text_bounds;
|
||||||
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
|
short line_height = (short)ctx.font.ascender - (short)ctx.font.descender;
|
||||||
@ -219,7 +216,7 @@ fn Point Ctx.center_text(&ctx, Rect text_bounds, Rect bounds)
|
|||||||
short dw = bounds.w - text_bounds.w;
|
short dw = bounds.w - text_bounds.w;
|
||||||
short dh = bounds.h - text_bounds.h;
|
short dh = bounds.h - text_bounds.h;
|
||||||
|
|
||||||
return Point{.x = dw/2, .y = dh/2};
|
return {.x = dw/2, .y = dh/2};
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check if the font is present in the context
|
// TODO: check if the font is present in the context
|
||||||
|
@ -45,10 +45,10 @@ const ModKeys KMOD_CTRL = {.lctrl = true, .rctrl = true};
|
|||||||
const ModKeys KMOD_SHIFT = {.lshift = true, .rshift = true};
|
const ModKeys KMOD_SHIFT = {.lshift = true, .rshift = true};
|
||||||
const ModKeys KMOD_ALT = {.lalt = true, .ralt = true};
|
const ModKeys KMOD_ALT = {.lalt = true, .ralt = true};
|
||||||
const ModKeys KMOD_GUI = {.lgui = true, .rgui = true};
|
const ModKeys KMOD_GUI = {.lgui = true, .rgui = true};
|
||||||
const ModKeys KMOD_NONE = ModKeys{};
|
const ModKeys KMOD_NONE = {};
|
||||||
const ModKeys KMOD_ANY = (ModKeys)(ModKeys.inner.max);
|
const ModKeys KMOD_ANY = (ModKeys)(ModKeys.inner.max);
|
||||||
|
|
||||||
const MouseButtons BTN_NONE = MouseButtons{};
|
const MouseButtons BTN_NONE = {};
|
||||||
const MouseButtons BTN_ANY = (MouseButtons)(MouseButtons.inner.max);
|
const MouseButtons BTN_ANY = (MouseButtons)(MouseButtons.inner.max);
|
||||||
const MouseButtons BTN_LEFT = {.btn_left = true};
|
const MouseButtons BTN_LEFT = {.btn_left = true};
|
||||||
const MouseButtons BTN_MIDDLE = {.btn_middle = true};
|
const MouseButtons BTN_MIDDLE = {.btn_middle = true};
|
||||||
@ -72,10 +72,10 @@ fn bool Ctx.check_key_combo(&ctx, ModKeys mod, String keys)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Window size was changed
|
// Window size was changed
|
||||||
fn void! Ctx.input_window_size(&ctx, short width, short height)
|
fn void? Ctx.input_window_size(&ctx, short width, short height)
|
||||||
{
|
{
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
return UgError.INVALID_SIZE?;
|
return INVALID_SIZE?;
|
||||||
}
|
}
|
||||||
ctx.input.events.resize = ctx.width != width || ctx.height != height;
|
ctx.input.events.resize = ctx.width != width || ctx.height != height;
|
||||||
ctx.width = width;
|
ctx.width = width;
|
||||||
|
@ -7,71 +7,71 @@ enum Layout {
|
|||||||
LAYOUT_ABSOLUTE,
|
LAYOUT_ABSOLUTE,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.layout_set_row(&ctx)
|
fn void? Ctx.layout_set_row(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
Elem *parent = ctx.cache.search(parent_id)!;
|
Elem *parent = ctx.cache.search(parent_id)!;
|
||||||
|
|
||||||
if (parent.type != ETYPE_DIV) {
|
if (parent.type != ETYPE_DIV) {
|
||||||
// what?
|
// what?
|
||||||
return UgError.UNEXPECTED_ELEMENT?;
|
return UNEXPECTED_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.div.layout = LAYOUT_ROW;
|
parent.div.layout = LAYOUT_ROW;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.layout_set_column(&ctx)
|
fn void? Ctx.layout_set_column(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
Elem *parent = ctx.cache.search(parent_id)!;
|
Elem *parent = ctx.cache.search(parent_id)!;
|
||||||
|
|
||||||
if (parent.type != ETYPE_DIV) {
|
if (parent.type != ETYPE_DIV) {
|
||||||
// what?
|
// what?
|
||||||
return UgError.UNEXPECTED_ELEMENT?;
|
return UNEXPECTED_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.div.layout = LAYOUT_COLUMN;
|
parent.div.layout = LAYOUT_COLUMN;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.layout_set_floating(&ctx)
|
fn void? Ctx.layout_set_floating(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
Elem *parent = ctx.cache.search(parent_id)!;
|
Elem *parent = ctx.cache.search(parent_id)!;
|
||||||
|
|
||||||
if (parent.type != ETYPE_DIV) {
|
if (parent.type != ETYPE_DIV) {
|
||||||
// what?
|
// what?
|
||||||
return UgError.UNEXPECTED_ELEMENT?;
|
return UNEXPECTED_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.div.layout = LAYOUT_FLOATING;
|
parent.div.layout = LAYOUT_FLOATING;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.layout_next_row(&ctx)
|
fn void? Ctx.layout_next_row(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
Elem *parent = ctx.cache.search(parent_id)!;
|
Elem *parent = ctx.cache.search(parent_id)!;
|
||||||
|
|
||||||
if (parent.type != ETYPE_DIV) {
|
if (parent.type != ETYPE_DIV) {
|
||||||
return UgError.UNEXPECTED_ELEMENT?;
|
return UNEXPECTED_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.div.origin_r = Point{
|
parent.div.origin_r = {
|
||||||
.x = parent.bounds.x,
|
.x = parent.bounds.x,
|
||||||
.y = parent.div.children_bounds.bottom_right().y,
|
.y = parent.div.children_bounds.bottom_right().y,
|
||||||
};
|
};
|
||||||
parent.div.origin_c = parent.div.origin_r;
|
parent.div.origin_c = parent.div.origin_r;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.layout_next_column(&ctx)
|
fn void? Ctx.layout_next_column(&ctx)
|
||||||
{
|
{
|
||||||
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
Id parent_id = ctx.tree.get(ctx.active_div)!;
|
||||||
Elem *parent = ctx.cache.search(parent_id)!;
|
Elem *parent = ctx.cache.search(parent_id)!;
|
||||||
|
|
||||||
if (parent.type != ETYPE_DIV) {
|
if (parent.type != ETYPE_DIV) {
|
||||||
return UgError.UNEXPECTED_ELEMENT?;
|
return UNEXPECTED_ELEMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.div.origin_c = Point{
|
parent.div.origin_c = {
|
||||||
.x = parent.div.children_bounds.bottom_right().x,
|
.x = parent.div.children_bounds.bottom_right().x,
|
||||||
.y = parent.bounds.y,
|
.y = parent.bounds.y,
|
||||||
};
|
};
|
||||||
@ -124,7 +124,7 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
|
|||||||
case LAYOUT_ABSOLUTE: // absolute position, this is a no-op, return the rect
|
case LAYOUT_ABSOLUTE: // absolute position, this is a no-op, return the rect
|
||||||
return rect;
|
return rect;
|
||||||
default: // error
|
default: // error
|
||||||
return Rect{};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Compute the parent's view
|
// 2. Compute the parent's view
|
||||||
@ -166,11 +166,11 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
|
|||||||
child_occupied = child_occupied.grow(rect.size());
|
child_occupied = child_occupied.grow(rect.size());
|
||||||
|
|
||||||
// 4. Update the parent's origin
|
// 4. Update the parent's origin
|
||||||
div.origin_r = Point{
|
div.origin_r = {
|
||||||
.x = child_occupied.bottom_right().x,
|
.x = child_occupied.bottom_right().x,
|
||||||
.y = origin.y,
|
.y = origin.y,
|
||||||
};
|
};
|
||||||
div.origin_c = Point{
|
div.origin_c = {
|
||||||
.x = origin.x,
|
.x = origin.x,
|
||||||
.y = child_occupied.bottom_right().y,
|
.y = child_occupied.bottom_right().y,
|
||||||
};
|
};
|
||||||
@ -191,6 +191,6 @@ fn Rect Ctx.position_element(&ctx, Elem *parent, Rect rect, bool style = false)
|
|||||||
if (child_placement.collides(parent_view)) {
|
if (child_placement.collides(parent_view)) {
|
||||||
return child_placement.off(parent.get_view_off().neg());
|
return child_placement.off(parent.get_view_off().neg());
|
||||||
} else {
|
} else {
|
||||||
return Rect{};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ macro bool Rect.contains(Rect a, Rect b)
|
|||||||
// returns the intersection of a and b
|
// returns the intersection of a and b
|
||||||
macro Rect Rect.intersection(Rect a, Rect b)
|
macro Rect Rect.intersection(Rect a, Rect b)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = (short)max(a.x, b.x),
|
.x = (short)max(a.x, b.x),
|
||||||
.y = (short)max(a.y, b.y),
|
.y = (short)max(a.y, b.y),
|
||||||
.w = (short)min(a.x+a.w, b.x+b.w) - (short)max(a.x, b.x),
|
.w = (short)min(a.x+a.w, b.x+b.w) - (short)max(a.x, b.x),
|
||||||
@ -39,7 +39,7 @@ macro bool Rect.is_null(Rect r) => r.x == 0 && r.y == 0 && r.x == 0 && r.w == 0;
|
|||||||
// returns the element-wise addition of r1 and r2
|
// returns the element-wise addition of r1 and r2
|
||||||
macro Rect Rect.add(Rect r1, Rect r2)
|
macro Rect Rect.add(Rect r1, Rect r2)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = r1.x + r2.x,
|
.x = r1.x + r2.x,
|
||||||
.y = r1.y + r2.y,
|
.y = r1.y + r2.y,
|
||||||
.w = r1.w + r2.w,
|
.w = r1.w + r2.w,
|
||||||
@ -50,7 +50,7 @@ macro Rect Rect.add(Rect r1, Rect r2)
|
|||||||
// returns the element-wise subtraction of r1 and r2
|
// returns the element-wise subtraction of r1 and r2
|
||||||
macro Rect Rect.sub(Rect r1, Rect r2)
|
macro Rect Rect.sub(Rect r1, Rect r2)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = r1.x - r2.x,
|
.x = r1.x - r2.x,
|
||||||
.y = r1.y - r2.y,
|
.y = r1.y - r2.y,
|
||||||
.w = r1.w - r2.w,
|
.w = r1.w - r2.w,
|
||||||
@ -61,7 +61,7 @@ macro Rect Rect.sub(Rect r1, Rect r2)
|
|||||||
// returns the element-wise multiplication of r1 and r2
|
// returns the element-wise multiplication of r1 and r2
|
||||||
macro Rect Rect.mul(Rect r1, Rect r2)
|
macro Rect Rect.mul(Rect r1, Rect r2)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = r1.x * r2.x,
|
.x = r1.x * r2.x,
|
||||||
.y = r1.y * r2.y,
|
.y = r1.y * r2.y,
|
||||||
.w = r1.w * r2.w,
|
.w = r1.w * r2.w,
|
||||||
@ -71,7 +71,7 @@ macro Rect Rect.mul(Rect r1, Rect r2)
|
|||||||
|
|
||||||
macro Point Rect.position(Rect r)
|
macro Point Rect.position(Rect r)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = r.x,
|
.x = r.x,
|
||||||
.y = r.y,
|
.y = r.y,
|
||||||
};
|
};
|
||||||
@ -79,7 +79,7 @@ macro Point Rect.position(Rect r)
|
|||||||
|
|
||||||
macro Point Rect.size(Rect r)
|
macro Point Rect.size(Rect r)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = r.w,
|
.x = r.w,
|
||||||
.y = r.h,
|
.y = r.h,
|
||||||
};
|
};
|
||||||
@ -87,7 +87,7 @@ macro Point Rect.size(Rect r)
|
|||||||
|
|
||||||
macro Rect Rect.max(Rect a, Rect b)
|
macro Rect Rect.max(Rect a, Rect b)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = max(a.x, b.x),
|
.x = max(a.x, b.x),
|
||||||
.y = max(a.y, b.y),
|
.y = max(a.y, b.y),
|
||||||
.w = max(a.w, b.w),
|
.w = max(a.w, b.w),
|
||||||
@ -97,7 +97,7 @@ macro Rect Rect.max(Rect a, Rect b)
|
|||||||
|
|
||||||
macro Rect Rect.min(Rect a, Rect b)
|
macro Rect Rect.min(Rect a, Rect b)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = min(a.x, b.x),
|
.x = min(a.x, b.x),
|
||||||
.y = min(a.y, b.y),
|
.y = min(a.y, b.y),
|
||||||
.w = min(a.w, b.w),
|
.w = min(a.w, b.w),
|
||||||
@ -108,7 +108,7 @@ macro Rect Rect.min(Rect a, Rect b)
|
|||||||
// Offset a rect by a point
|
// Offset a rect by a point
|
||||||
macro Rect Rect.off(Rect r, Point p)
|
macro Rect Rect.off(Rect r, Point p)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = r.x + p.x,
|
.x = r.x + p.x,
|
||||||
.y = r.y + p.y,
|
.y = r.y + p.y,
|
||||||
.w = r.w,
|
.w = r.w,
|
||||||
@ -119,7 +119,7 @@ macro Rect Rect.off(Rect r, Point p)
|
|||||||
// Resize a rect width and height
|
// Resize a rect width and height
|
||||||
macro Rect Rect.grow(Rect r, Point p)
|
macro Rect Rect.grow(Rect r, Point p)
|
||||||
{
|
{
|
||||||
return Rect{
|
return {
|
||||||
.x = r.x,
|
.x = r.x,
|
||||||
.y = r.y,
|
.y = r.y,
|
||||||
.w = r.w + p.x,
|
.w = r.w + p.x,
|
||||||
@ -130,7 +130,7 @@ macro Rect Rect.grow(Rect r, Point p)
|
|||||||
// Return the bottom-right corner of a rectangle
|
// Return the bottom-right corner of a rectangle
|
||||||
macro Point Rect.bottom_right(Rect r)
|
macro Point Rect.bottom_right(Rect r)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = r.x + r.w,
|
.x = r.x + r.w,
|
||||||
.y = r.y + r.h,
|
.y = r.y + r.h,
|
||||||
};
|
};
|
||||||
@ -153,7 +153,7 @@ macro bool Point.in_rect(Point p, Rect r)
|
|||||||
|
|
||||||
macro Point Point.add(Point a, Point b)
|
macro Point Point.add(Point a, Point b)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = a.x + b.x,
|
.x = a.x + b.x,
|
||||||
.y = a.y + b.y,
|
.y = a.y + b.y,
|
||||||
};
|
};
|
||||||
@ -161,17 +161,17 @@ macro Point Point.add(Point a, Point b)
|
|||||||
|
|
||||||
macro Point Point.sub(Point a, Point b)
|
macro Point Point.sub(Point a, Point b)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = a.x - b.x,
|
.x = a.x - b.x,
|
||||||
.y = a.y - b.y,
|
.y = a.y - b.y,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro Point Point.neg(Point p) => Point{-p.x, -p.y};
|
macro Point Point.neg(Point p) => {-p.x, -p.y};
|
||||||
|
|
||||||
macro Point Point.max(Point a, Point b)
|
macro Point Point.max(Point a, Point b)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = max(a.x, b.x),
|
.x = max(a.x, b.x),
|
||||||
.y = max(a.y, b.y),
|
.y = max(a.y, b.y),
|
||||||
};
|
};
|
||||||
@ -179,7 +179,7 @@ macro Point Point.max(Point a, Point b)
|
|||||||
|
|
||||||
macro Point Point.min(Point a, Point b)
|
macro Point Point.min(Point a, Point b)
|
||||||
{
|
{
|
||||||
return Point{
|
return {
|
||||||
.x = min(a.x, b.x),
|
.x = min(a.x, b.x),
|
||||||
.y = min(a.y, b.y),
|
.y = min(a.y, b.y),
|
||||||
};
|
};
|
||||||
@ -195,7 +195,7 @@ struct Color{
|
|||||||
|
|
||||||
macro Color uint.to_rgba(uint u)
|
macro Color uint.to_rgba(uint u)
|
||||||
{
|
{
|
||||||
return Color{
|
return {
|
||||||
.r = (char)((u >> 24) & 0xff),
|
.r = (char)((u >> 24) & 0xff),
|
||||||
.g = (char)((u >> 16) & 0xff),
|
.g = (char)((u >> 16) & 0xff),
|
||||||
.b = (char)((u >> 8) & 0xff),
|
.b = (char)((u >> 8) & 0xff),
|
||||||
|
@ -16,7 +16,7 @@ struct ElemSlider {
|
|||||||
<*
|
<*
|
||||||
@require value != null
|
@require value != null
|
||||||
*>
|
*>
|
||||||
fn ElemEvents! Ctx.slider_hor(&ctx,
|
fn ElemEvents? Ctx.slider_hor(&ctx,
|
||||||
String label,
|
String label,
|
||||||
Rect size,
|
Rect size,
|
||||||
float* value,
|
float* value,
|
||||||
@ -35,7 +35,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx,
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_SLIDER;
|
elem.type = ETYPE_SLIDER;
|
||||||
} else if (elem.type != ETYPE_SLIDER) {
|
} else if (elem.type != ETYPE_SLIDER) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Layout
|
// 2. Layout
|
||||||
@ -79,7 +79,7 @@ fn ElemEvents! Ctx.slider_hor(&ctx,
|
|||||||
* | |
|
* | |
|
||||||
* +-+
|
* +-+
|
||||||
*/
|
*/
|
||||||
fn ElemEvents! Ctx.slider_ver(&ctx,
|
fn ElemEvents? Ctx.slider_ver(&ctx,
|
||||||
String label,
|
String label,
|
||||||
Rect size,
|
Rect size,
|
||||||
float* value,
|
float* value,
|
||||||
@ -98,7 +98,7 @@ fn ElemEvents! Ctx.slider_ver(&ctx,
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_SLIDER;
|
elem.type = ETYPE_SLIDER;
|
||||||
} else if (elem.type != ETYPE_SLIDER) {
|
} else if (elem.type != ETYPE_SLIDER) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Layout
|
// 2. Layout
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module ugui;
|
module ugui;
|
||||||
|
|
||||||
|
import std::core::mem::allocator;
|
||||||
import std::collections::map;
|
import std::collections::map;
|
||||||
import std::io;
|
import std::io;
|
||||||
import mqoi;
|
import mqoi;
|
||||||
@ -20,7 +21,7 @@ struct Sprite {
|
|||||||
ushort w, h;
|
ushort w, h;
|
||||||
}
|
}
|
||||||
|
|
||||||
def SpriteMap = map::HashMap(<Id, Sprite>);
|
alias SpriteMap = map::HashMap{Id, Sprite};
|
||||||
|
|
||||||
struct SpriteAtlas {
|
struct SpriteAtlas {
|
||||||
Id id;
|
Id id;
|
||||||
@ -34,20 +35,20 @@ struct ElemSprite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// name: some examples are "icons" or "images"
|
// name: some examples are "icons" or "images"
|
||||||
fn void! SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height)
|
fn void? SpriteAtlas.init(&this, String name, AtlasType type, ushort width, ushort height)
|
||||||
{
|
{
|
||||||
// FIXME: for now only R8G8B8A8 format is supported
|
// FIXME: for now only R8G8B8A8 format is supported
|
||||||
if (type != ATLAS_R8G8B8A8) {
|
if (type != ATLAS_R8G8B8A8) {
|
||||||
return UgAtlasError.INVALID_TYPE?;
|
return INVALID_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
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.new_init(capacity: SRITES_PER_ATLAS);
|
this.sprites.init(allocator::heap(), capacity: SRITES_PER_ATLAS);
|
||||||
this.should_update = false;
|
this.should_update = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! SpriteAtlas.free(&this)
|
fn void? SpriteAtlas.free(&this)
|
||||||
{
|
{
|
||||||
this.atlas.free();
|
this.atlas.free();
|
||||||
this.sprites.free();
|
this.sprites.free();
|
||||||
@ -55,7 +56,7 @@ fn void! SpriteAtlas.free(&this)
|
|||||||
|
|
||||||
// FIXME: this should throw an error when a different pixel format than the atlas' is used
|
// FIXME: this should throw an error when a different pixel format than the atlas' is used
|
||||||
// or convert from the source's pixel format to the atlas'
|
// or convert from the source's pixel format to the atlas'
|
||||||
fn Sprite*! SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixels, ushort w, ushort h, ushort stride)
|
fn Sprite*? SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixels, ushort w, ushort h, ushort stride)
|
||||||
{
|
{
|
||||||
Sprite s;
|
Sprite s;
|
||||||
s.id = name.hash();
|
s.id = name.hash();
|
||||||
@ -70,18 +71,21 @@ fn Sprite*! SpriteAtlas.insert(&this, String name, SpriteType type, char[] pixel
|
|||||||
return this.sprites.get_ref(s.id);
|
return this.sprites.get_ref(s.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Sprite*! SpriteAtlas.get(&this, String name)
|
fn Sprite*? SpriteAtlas.get(&this, String name)
|
||||||
{
|
{
|
||||||
Id id = name.hash();
|
Id id = name.hash();
|
||||||
return this.sprites.get_ref(id);
|
return this.sprites.get_ref(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn Sprite*! SpriteAtlas.get_by_id(&this, Id id)
|
fn Sprite*? SpriteAtlas.get_by_id(&this, Id id)
|
||||||
{
|
{
|
||||||
return this.sprites.get_ref(id);
|
return this.sprites.get_ref(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.sprite_atlas_create(&ctx, String name, AtlasType type, ushort w, ushort h)
|
macro Rect Sprite.rect(s) => {0,0,s.w,s.h};
|
||||||
|
macro Rect Sprite.uv(s) => {s.u,s.v,s.w,s.h};
|
||||||
|
|
||||||
|
fn void? Ctx.sprite_atlas_create(&ctx, String name, AtlasType type, ushort w, ushort h)
|
||||||
{
|
{
|
||||||
ctx.sprite_atlas.init(name, type, w, h)!;
|
ctx.sprite_atlas.init(name, type, w, h)!;
|
||||||
}
|
}
|
||||||
@ -91,12 +95,12 @@ fn Id Ctx.get_sprite_atlas_id(&ctx, String name)
|
|||||||
return name.hash();
|
return name.hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride, SpriteType type = SPRITE_NORMAL)
|
fn void? Ctx.import_sprite_memory(&ctx, String name, char[] pixels, ushort w, ushort h, ushort stride, SpriteType type = SPRITE_NORMAL)
|
||||||
{
|
{
|
||||||
ctx.sprite_atlas.insert(name, type, pixels, w, h, stride)!;
|
ctx.sprite_atlas.insert(name, type, pixels, w, h, stride)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL)
|
fn void? Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType type = SPRITE_NORMAL)
|
||||||
{
|
{
|
||||||
mqoi::Desc image_desc;
|
mqoi::Desc image_desc;
|
||||||
uint w, h;
|
uint w, h;
|
||||||
@ -108,7 +112,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
|
|||||||
mqoi::desc_push(&image_desc, file.read_byte()!);
|
mqoi::desc_push(&image_desc, file.read_byte()!);
|
||||||
}
|
}
|
||||||
if (mqoi::desc_verify(&image_desc, &w, &h) != 0) {
|
if (mqoi::desc_verify(&image_desc, &w, &h) != 0) {
|
||||||
return IoError.FILE_NOT_VALID?;
|
return io::FILE_NOT_VALID?;
|
||||||
}
|
}
|
||||||
|
|
||||||
mqoi::Dec dec;
|
mqoi::Dec dec;
|
||||||
@ -123,7 +127,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
|
|||||||
mqoi::dec_push(&dec, file.read_byte()!);
|
mqoi::dec_push(&dec, file.read_byte()!);
|
||||||
|
|
||||||
while ((px = mqoi::dec_pop(&dec)) != null) {
|
while ((px = mqoi::dec_pop(&dec)) != null) {
|
||||||
pixels[idx..idx+3] = px.value;
|
pixels[idx..idx+3] = px.value[..];
|
||||||
idx += 4;
|
idx += 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -131,7 +135,7 @@ fn void! Ctx.import_sprite_file_qoi(&ctx, String name, String path, SpriteType t
|
|||||||
ctx.sprite_atlas.insert(name, type, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
ctx.sprite_atlas.insert(name, type, pixels, (ushort)w, (ushort)h, (ushort)w)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
fn void? Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
@ -143,7 +147,7 @@ fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
|||||||
if (elem.flags.is_new) {
|
if (elem.flags.is_new) {
|
||||||
elem.type = ETYPE_SPRITE;
|
elem.type = ETYPE_SPRITE;
|
||||||
} else if (elem.type != ETYPE_SPRITE) {
|
} else if (elem.type != ETYPE_SPRITE) {
|
||||||
return UgError.WRONG_ELEMENT_TYPE?;
|
return WRONG_ELEMENT_TYPE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
||||||
@ -163,11 +167,9 @@ fn void! Ctx.draw_sprite(&ctx, String label, String name, Point off = {0,0})
|
|||||||
return ctx.push_sprite(elem.bounds, uv, tex_id)!;
|
return ctx.push_sprite(elem.bounds, uv, tex_id)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.draw_sprite_raw(&ctx, String name, Rect bounds)
|
fn void? Ctx.draw_sprite_raw(&ctx, String name, Rect bounds)
|
||||||
{
|
{
|
||||||
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
Sprite* sprite = ctx.sprite_atlas.get(name)!;
|
||||||
Rect uv = { sprite.u, sprite.v, sprite.w, sprite.h };
|
|
||||||
//Rect bounds = Rect{ 0, 0, sprite.w, sprite.h }.off(off);
|
|
||||||
Id tex_id = ctx.sprite_atlas.id;
|
Id tex_id = ctx.sprite_atlas.id;
|
||||||
return ctx.push_sprite(bounds, uv, tex_id, type: sprite.type)!;
|
return ctx.push_sprite(bounds, sprite.uv(), tex_id, type: sprite.type)!;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ struct ElemText {
|
|||||||
char* str;
|
char* str;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! Ctx.text_unbounded(&ctx, String label, String text)
|
fn void? Ctx.text_unbounded(&ctx, String label, String text)
|
||||||
{
|
{
|
||||||
Id id = ctx.gen_id(label)!;
|
Id id = ctx.gen_id(label)!;
|
||||||
|
|
||||||
|
64
src/vtree.c3
64
src/vtree.c3
@ -1,4 +1,4 @@
|
|||||||
module vtree(<ElemType>);
|
module vtree{ElemType};
|
||||||
|
|
||||||
import std::core::mem;
|
import std::core::mem;
|
||||||
import std::io;
|
import std::io;
|
||||||
@ -9,13 +9,7 @@ struct VTree {
|
|||||||
isz[] refs, ordered_refs;
|
isz[] refs, ordered_refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
fault VTreeError {
|
faultdef CANNOT_SHRINK, INVALID_REFERENCE, TREE_FULL, REFERENCE_NOT_PRESENT, INVALID_ARGUMENT;
|
||||||
CANNOT_SHRINK,
|
|
||||||
INVALID_REFERENCE,
|
|
||||||
TREE_FULL,
|
|
||||||
REFERENCE_NOT_PRESENT,
|
|
||||||
INVALID_ARGUMENT,
|
|
||||||
}
|
|
||||||
|
|
||||||
macro VTree.ref_is_valid(&tree, isz ref) { return (ref >= 0 && ref < tree.refs.len); }
|
macro VTree.ref_is_valid(&tree, isz ref) { return (ref >= 0 && ref < tree.refs.len); }
|
||||||
macro VTree.ref_is_present(&tree, isz ref) { return tree.refs[ref] >= 0; }
|
macro VTree.ref_is_present(&tree, isz ref) { return tree.refs[ref] >= 0; }
|
||||||
@ -27,11 +21,11 @@ macro @zero()
|
|||||||
$if $assignable(0, ElemType):
|
$if $assignable(0, ElemType):
|
||||||
return 0;
|
return 0;
|
||||||
$else
|
$else
|
||||||
return ElemType{};
|
return {};
|
||||||
$endif
|
$endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! VTree.init(&tree, usz size)
|
fn void? VTree.init(&tree, usz size)
|
||||||
{
|
{
|
||||||
tree.vector = mem::new_array(ElemType, size);
|
tree.vector = mem::new_array(ElemType, size);
|
||||||
defer catch { (void)mem::free(tree.vector); }
|
defer catch { (void)mem::free(tree.vector); }
|
||||||
@ -74,7 +68,7 @@ fn void VTree.pack(&tree)
|
|||||||
tree.vector[free_spot] = tree.vector[i];
|
tree.vector[free_spot] = tree.vector[i];
|
||||||
tree.refs[free_spot] = tree.refs[i];
|
tree.refs[free_spot] = tree.refs[i];
|
||||||
|
|
||||||
tree.vector[i] = @zero();
|
tree.vector[i] = {};
|
||||||
tree.refs[i] = -1;
|
tree.refs[i] = -1;
|
||||||
|
|
||||||
// and move all references
|
// and move all references
|
||||||
@ -90,18 +84,18 @@ fn void VTree.pack(&tree)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn void! VTree.resize(&tree, usz newsize)
|
fn void? VTree.resize(&tree, usz newsize)
|
||||||
{
|
{
|
||||||
// return error when shrinking with too many elements
|
// return error when shrinking with too many elements
|
||||||
if (newsize < tree.elements) {
|
if (newsize < tree.elements) {
|
||||||
return VTreeError.CANNOT_SHRINK?;
|
return CANNOT_SHRINK?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pack the vector when shrinking to avoid data loss
|
// pack the vector when shrinking to avoid data loss
|
||||||
if ((int)newsize < tree.size()) {
|
if ((int)newsize < tree.size()) {
|
||||||
// FIXME: packing destroys all references to elements of vec
|
// FIXME: packing destroys all references to elements of vec
|
||||||
// so shrinking may cause dangling pointers
|
// so shrinking may cause dangling pointers
|
||||||
return VTreeError.CANNOT_SHRINK?;
|
return CANNOT_SHRINK?;
|
||||||
}
|
}
|
||||||
|
|
||||||
usz old_size = tree.size();
|
usz old_size = tree.size();
|
||||||
@ -122,22 +116,22 @@ fn void! VTree.resize(&tree, usz newsize)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add an element to the tree, return it's ref
|
// add an element to the tree, return it's ref
|
||||||
fn isz! VTree.add(&tree, ElemType elem, isz parent)
|
fn isz? VTree.add(&tree, ElemType elem, isz parent)
|
||||||
{
|
{
|
||||||
// invalid parent
|
// invalid parent
|
||||||
if (!tree.ref_is_valid(parent)) {
|
if (!tree.ref_is_valid(parent)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// no space left
|
// no space left
|
||||||
if (tree.elements >= tree.size()) {
|
if (tree.elements >= tree.size()) {
|
||||||
return VTreeError.TREE_FULL?;
|
return TREE_FULL?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if the parent exists
|
// check if the parent exists
|
||||||
// if there are no elements in the tree the first add will set the root
|
// if there are no elements in the tree the first add will set the root
|
||||||
if (!tree.ref_is_present(parent) && tree.elements != 0) {
|
if (!tree.ref_is_present(parent) && tree.elements != 0) {
|
||||||
return VTreeError.REFERENCE_NOT_PRESENT?;
|
return REFERENCE_NOT_PRESENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the first free spot
|
// get the first free spot
|
||||||
@ -149,7 +143,7 @@ fn isz! VTree.add(&tree, ElemType elem, isz parent)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (free_spot < 0) {
|
if (free_spot < 0) {
|
||||||
return VTreeError.TREE_FULL?;
|
return TREE_FULL?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// finally add the element
|
// finally add the element
|
||||||
@ -162,10 +156,10 @@ fn isz! VTree.add(&tree, ElemType elem, isz parent)
|
|||||||
|
|
||||||
// prune the tree starting from the ref
|
// prune the tree starting from the ref
|
||||||
// returns the number of pruned elements
|
// returns the number of pruned elements
|
||||||
fn usz! VTree.prune(&tree, isz ref)
|
fn usz? VTree.prune(&tree, isz ref)
|
||||||
{
|
{
|
||||||
if (!tree.ref_is_valid(ref)) {
|
if (!tree.ref_is_valid(ref)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tree.ref_is_present(ref)) {
|
if (!tree.ref_is_present(ref)) {
|
||||||
@ -196,10 +190,10 @@ fn usz VTree.nuke(&tree)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find the size of the subtree starting from ref
|
// find the size of the subtree starting from ref
|
||||||
fn usz! VTree.subtree_size(&tree, isz ref)
|
fn usz? VTree.subtree_size(&tree, isz ref)
|
||||||
{
|
{
|
||||||
if (!tree.ref_is_valid(ref)) {
|
if (!tree.ref_is_valid(ref)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tree.ref_is_present(ref)) {
|
if (!tree.ref_is_present(ref)) {
|
||||||
@ -218,20 +212,20 @@ fn usz! VTree.subtree_size(&tree, isz ref)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// iterate through the first level children, use a cursor like strtok_r
|
// iterate through the first level children, use a cursor like strtok_r
|
||||||
fn isz! VTree.children_it(&tree, isz parent, isz *cursor)
|
fn isz? VTree.children_it(&tree, isz parent, isz *cursor)
|
||||||
{
|
{
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
return VTreeError.INVALID_ARGUMENT?;
|
return INVALID_ARGUMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the cursor is out of bounds then we are done for sure
|
// if the cursor is out of bounds then we are done for sure
|
||||||
if (!tree.ref_is_valid(*cursor)) {
|
if (!tree.ref_is_valid(*cursor)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// same for the parent, if it's invalid it can't have children
|
// same for the parent, if it's invalid it can't have children
|
||||||
if (!tree.ref_is_valid(parent) || !tree.ref_is_present(parent)) {
|
if (!tree.ref_is_valid(parent) || !tree.ref_is_present(parent)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find the first child, update the cursor and return the ref
|
// find the first child, update the cursor and return the ref
|
||||||
@ -256,10 +250,10 @@ fn isz! VTree.children_it(&tree, isz parent, isz *cursor)
|
|||||||
* / \ [6]
|
* / \ [6]
|
||||||
* [4] [5]
|
* [4] [5]
|
||||||
*/
|
*/
|
||||||
fn isz! VTree.level_order_it(&tree, isz ref, isz *cursor)
|
fn isz? VTree.level_order_it(&tree, isz ref, isz *cursor)
|
||||||
{
|
{
|
||||||
if (cursor == null) {
|
if (cursor == null) {
|
||||||
return VTreeError.INVALID_ARGUMENT?;
|
return INVALID_ARGUMENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
isz[] queue = tree.ordered_refs;
|
isz[] queue = tree.ordered_refs;
|
||||||
@ -303,27 +297,27 @@ fn isz! VTree.level_order_it(&tree, isz ref, isz *cursor)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn isz! VTree.parentof(&tree, isz ref)
|
fn isz? VTree.parentof(&tree, isz ref)
|
||||||
{
|
{
|
||||||
if (!tree.ref_is_valid(ref)) {
|
if (!tree.ref_is_valid(ref)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tree.ref_is_present(ref)) {
|
if (!tree.ref_is_present(ref)) {
|
||||||
return VTreeError.REFERENCE_NOT_PRESENT?;
|
return REFERENCE_NOT_PRESENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree.refs[ref];
|
return tree.refs[ref];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ElemType! VTree.get(&tree, isz ref)
|
fn ElemType? VTree.get(&tree, isz ref)
|
||||||
{
|
{
|
||||||
if (!tree.ref_is_valid(ref)) {
|
if (!tree.ref_is_valid(ref)) {
|
||||||
return VTreeError.INVALID_REFERENCE?;
|
return INVALID_REFERENCE?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tree.ref_is_present(ref)) {
|
if (!tree.ref_is_present(ref)) {
|
||||||
return VTreeError.REFERENCE_NOT_PRESENT?;
|
return REFERENCE_NOT_PRESENT?;
|
||||||
}
|
}
|
||||||
|
|
||||||
return tree.vector[ref];
|
return tree.vector[ref];
|
||||||
|
Loading…
Reference in New Issue
Block a user