Compare commits

...

2 commits

2 changed files with 99 additions and 75 deletions

View file

@ -53,10 +53,11 @@ struct Component {
} stack;
} data;
// this is filled automatically (unless you use the `_manual` functions)
// this is filled automatically when you use stacks (unless you use the `_manual` functions)
struct Component *next;
struct Component *focus_prev;
struct Component *focus_next;
// this is always filled automatically
// TODO can remove one of these
struct rect rect;
struct rect content_rect;
};

169
src/app.c
View file

@ -73,33 +73,6 @@ move_up:
return true;
}
struct component_focus {
struct Component *root;
struct Component *curr;
struct component_iter it;
};
// Cycles to the next component, which will be stored in `focus->curr`.
void component_focus_cycle(struct component_focus *focus) {
start:
while (component_iter_next(&focus->it, &focus->curr)) {
switch (focus->curr->type) {
case CPNT_TYPE_CANVAS:
case CPNT_TYPE_BUTTON:
case CPNT_TYPE_TEXT_INPUT:
return;
default:
break; // keep iterating, because object cannot be focused
}
}
// all the way through. reset.
focus->it = (struct component_iter) {
.cpnt = focus->root,
.stack_depth = 0,
};
goto start;
}
// App instances are kept in a doubly linked list.
// `instance_root` is the topmost app, `instance_root->next_app` the one below, and so forth.
// `instance_root->prev_app` is the bottom-most app.
@ -113,7 +86,7 @@ struct app_instance {
struct tex framebuf;
// each instance has its own space for font rendering
struct tex fontbuf;
struct component_focus focus;
struct Component *in_focus;
struct app_instance *next_app;
struct app_instance *prev_app;
};
@ -179,9 +152,14 @@ void app_init_global(void) {
atomic_store(&app_initialized, true);
}
struct layout_state {
struct Component *first_focus_elem;
struct Component *focus_prev;
};
// Simple recursive downward layout of statically sized components.
// Every component records its position relative to the app origin in `cpnt->rect`.
void layout(struct Component *cpnt, int rootx, int rooty) {
void layout_r(struct Component *cpnt, int rootx, int rooty, struct layout_state *state) {
unsigned fh = app_text_height();
unsigned max_width = 0;
unsigned max_height = 0;
@ -189,6 +167,27 @@ void layout(struct Component *cpnt, int rootx, int rooty) {
unsigned core_width;
unsigned core_height;
int x = rootx, y = rooty;
// focus chain
switch (cpnt->type) {
case CPNT_TYPE_TEXT_INPUT: // fallthrough
case CPNT_TYPE_BUTTON: // fallthrough
case CPNT_TYPE_CANVAS:
if (state->focus_prev) {
state->focus_prev->focus_next = cpnt;
cpnt->focus_prev = state->focus_prev;
state->focus_prev = cpnt;
} else {
state->focus_prev = cpnt;
state->first_focus_elem = cpnt;
}
break;
case CPNT_TYPE_LABEL: // fallthrough
case CPNT_TYPE_STACK:
break;
}
// actual layout
switch (cpnt->type) {
case CPNT_TYPE_TEXT_INPUT:
core_width = cpnt->data.text_input.width;
@ -208,7 +207,7 @@ void layout(struct Component *cpnt, int rootx, int rooty) {
break;
case CPNT_TYPE_STACK:
for (struct Component *child = cpnt->data.stack.first_child ; child; child = child->next) {
layout(child, x, y);
layout_r(child, x, y, state);
if (cpnt->data.stack.horizontal) {
x += RECT_WIDTH(child->rect);
} else {
@ -236,6 +235,25 @@ void layout(struct Component *cpnt, int rootx, int rooty) {
cpnt->content_rect = RECT_XYWH(rootx + pad, rooty + pad, core_width, core_height);
}
struct Component *layout(struct Component *root) {
struct layout_state state = {
.first_focus_elem = NULL,
.focus_prev = NULL,
};
layout_r(root, 0, 0, &state);
// we need at least one focusable element
ASSERT(state.first_focus_elem);
ASSERT(state.focus_prev);
// close chain
if (state.first_focus_elem->focus_next == NULL) {
// was the only element
state.first_focus_elem->focus_next = state.first_focus_elem;
}
state.first_focus_elem->focus_prev = state.focus_prev;
state.focus_prev->focus_next = state.first_focus_elem;
return state.first_focus_elem;
}
static void draw_border(void *app, struct rect rect, struct color col) {
app_draw_rect(app, col, &RECT_XYWH(rect.x, rect.y, RECT_WIDTH(rect), 1), NULL);
app_draw_rect(app, col, &RECT_XYWH(rect.x, rect.y + RECT_HEIGHT(rect) - 1, RECT_WIDTH(rect), 1), NULL);
@ -428,6 +446,8 @@ unsigned app_border_size(void) {
static void app_component_redraw(void *self, struct Component *cpnt, bool border);
extern struct app_template app_launcher_template;
bool app_push(struct app_template *template, void *arg) {
ASSERT(template != NULL);
struct AppCreation creation = template->create();
@ -436,7 +456,7 @@ bool app_push(struct app_template *template, void *arg) {
}
// layout
layout(creation.root, 0, 0);
struct Component *in_focus = layout(creation.root);
struct rect dim = creation.root->rect;
void *fontbuf = slab_cache_alloc(&fontbuf_slab_cache);
@ -445,11 +465,14 @@ bool app_push(struct app_template *template, void *arg) {
// de-focus current element
if (instance_root) {
instance_focus_undraw(instance_root);
component_focus_undraw(instance_root->app, instance_root->focus.curr);
component_focus_undraw(instance_root->app, instance_root->in_focus);
}
// move to center
if (template != &app_launcher_template) {
rect_translate(&dim, (fb_width() - RECT_WIDTH(dim))/2, (fb_height() - RECT_HEIGHT(dim))/2);
}
struct component_focus focus = { .root = creation.root, .it = { .cpnt = creation.root, .stack_depth = 0}};
component_focus_cycle(&focus);
// TODO this structure should be locked
struct app_instance *new_instance = slab_cache_alloc(&instance_slab_cache);
ASSERT(new_instance != NULL);
@ -466,7 +489,7 @@ bool app_push(struct app_template *template, void *arg) {
font_height(&app_font),
fontbuf
),
.focus = focus,
.in_focus = in_focus,
.next_app = instance_root != NULL ? instance_root : new_instance,
.prev_app = instance_root != NULL ? instance_root->prev_app : new_instance
};
@ -483,7 +506,7 @@ bool app_push(struct app_template *template, void *arg) {
app_component_redraw(creation.ptr, c, true);
}
// focus current
component_focus_draw(new_instance->app, new_instance->focus.curr);
component_focus_draw(new_instance->app, new_instance->in_focus);
instance_focus_draw(new_instance);
if (template->init)
@ -503,13 +526,7 @@ void app_pop(void) {
kern_free(ins->framebuf.data);
slab_cache_free(ins->fontbuf.data);
struct Component *next;
struct Component *curr = ins->focus.root;
while (curr != NULL) {
next = curr->next;
slab_cache_free(curr);
curr = next;
}
// TODO free components (need to free sub-components too)
app_redraw_below(uncovered, ins);
if (ins == ins->next_app) {
@ -533,44 +550,42 @@ void app_translate(struct app_instance *ins, int dx, int dy) {
fb_damage(ins->pos);
}
extern struct app_template app_launcher_template;
void app_key_event_listener(struct key_event event) {
struct app_instance *ins = instance_root;
if (ins == NULL) {
return;
}
if (event.code == KEY_ARROW_LEFT && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_ARROW_LEFT && (event.mod & KEY_MOD_LCTRL)) {
if (event.pressed && ins->template != &app_launcher_template) {
app_translate(ins, -10, 0);
}
return;
}
if (event.code == KEY_ARROW_RIGHT && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_ARROW_RIGHT && (event.mod & KEY_MOD_LCTRL)) {
if (event.pressed && ins->template != &app_launcher_template) {
app_translate(ins, 10, 0);
}
return;
}
if (event.code == KEY_ARROW_UP && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_ARROW_UP && (event.mod & KEY_MOD_LCTRL)) {
if (event.pressed && ins->template != &app_launcher_template) {
app_translate(ins, 0, -10);
}
return;
}
if (event.code == KEY_ARROW_DOWN && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_ARROW_DOWN && (event.mod & KEY_MOD_LCTRL)) {
if (event.pressed && ins->template != &app_launcher_template) {
app_translate(ins, 0, 10);
}
return;
}
if (event.code == KEY_TAB && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_TAB && (event.mod & KEY_MOD_LCTRL)) {
// select next app to be the current one now (natural rotation, easy for now)
if (event.pressed) {
// remove focus from the thing it's currently on
instance_focus_undraw(ins);
component_focus_undraw(ins->app, ins->focus.curr);
component_focus_undraw(ins->app, ins->in_focus);
// switch app and redraw everything that may have been (un)covered now.
#define WINDOWS_ROTATE_BTT
@ -590,7 +605,7 @@ void app_key_event_listener(struct key_event event) {
}
return;
}
if (event.code == KEY_Q && (event.mod & KEY_MOD_SHIFT)) {
if (event.code == KEY_Q && (event.mod & KEY_MOD_LCTRL)) {
if (event.pressed) {
if (instance_root->template != &app_launcher_template) {
// never delete the launcher app
@ -600,16 +615,24 @@ void app_key_event_listener(struct key_event event) {
return;
}
if (event.code == KEY_TAB && event.pressed && (event.mod & KEY_MOD_LSHIFT)) {
// focus cycle
component_focus_undraw(ins->app, ins->in_focus);
ins->in_focus = ins->in_focus->focus_prev;
component_focus_draw(ins->app, ins->in_focus);
instance_focus_draw(ins);
return;
}
if (event.code == KEY_TAB && event.pressed) {
// focus cycle
component_focus_undraw(ins->app, ins->focus.curr);
component_focus_cycle(&ins->focus);
component_focus_draw(ins->app, ins->focus.curr);
component_focus_undraw(ins->app, ins->in_focus);
ins->in_focus = ins->in_focus->focus_next;
component_focus_draw(ins->app, ins->in_focus);
instance_focus_draw(ins);
return;
}
struct Component *focus = ins->focus.curr;
struct Component *focus = ins->in_focus;
if (focus->type == CPNT_TYPE_BUTTON) {
if (event.code == KEY_ENTER && event.pressed) {
@ -659,24 +682,23 @@ void app_mouse_event_listener(int8_t mouse_dx, int8_t mouse_dy, uint8_t buttons)
// search for element that was hit
int mouse_rel_x = mouse_x - instance_root->pos.x;
int mouse_rel_y = mouse_y - instance_root->pos.y;
struct component_focus focus = { .root = instance_root->focus.root, .it = { .cpnt = instance_root->focus.root, .stack_depth = 0}};
component_focus_cycle(&focus);
struct Component *start = focus.curr;
struct Component *start = instance_root->in_focus;
struct Component *curr = instance_root->in_focus;
struct Component *found = NULL;
do {
if (RECT_CONTAINS(focus.curr->rect, mouse_rel_x, mouse_rel_y)) {
found = focus.curr;
if (RECT_CONTAINS(curr->rect, mouse_rel_x, mouse_rel_y)) {
found = curr;
break;
}
component_focus_cycle(&focus);
} while (focus.curr != start);
curr = curr->focus_next;
} while (curr != start);
if (found != NULL) {
if ((buttons_just_pressed & MOUSE_BUTTON_LEFT) && instance_root->focus.curr != found) {
if ((buttons_just_pressed & MOUSE_BUTTON_LEFT) && instance_root->in_focus != found) {
// re-focus to hit element
component_focus_undraw(instance_root->app, instance_root->focus.curr);
instance_root->focus = focus;
component_focus_draw(instance_root->app, instance_root->focus.curr);
component_focus_undraw(instance_root->app, instance_root->in_focus);
instance_root->in_focus = found;
component_focus_draw(instance_root->app, instance_root->in_focus);
instance_focus_draw(instance_root);
}
@ -780,18 +802,19 @@ static struct AppCreation app_launcher_create(void) {
struct Component *welcome_label = app_component_new_label("Welcome to KarlOS!");
struct Component *cl0 = app_component_new_label("window controls:");
struct Component *cl1 = app_component_new_label("SHIFT+ARROW: move");
struct Component *cl2 = app_component_new_label("SHIFT+TAB: cycle");
struct Component *cl3 = app_component_new_label("SHIFT+Q: close");
struct Component *cl1 = app_component_new_label("LCTRL+ARROW: move");
struct Component *cl2 = app_component_new_label("LCTRL+TAB: cycle");
struct Component *cl3 = app_component_new_label("LCTRL+Q: close");
struct Component *cl4 = app_component_new_label("inside window:");
struct Component *cl5 = app_component_new_label("TAB: cycle");
struct Component *cl6 = app_component_new_label("you can use mouse");
struct Component *cl6 = app_component_new_label("SHIFT+TAB: cycle");
struct Component *cl7 = app_component_new_label("you can use mouse");
struct Component *launch_label = app_component_new_label("launch:");
struct Component *label = app_component_new_label(known_apps[app->current_app].name);
struct Component *btn_next = app_component_new_button("next", button_next);
struct Component *btn_launch = app_component_new_button("launch", button_launch);
struct Component *stack = app_component_new_stack(false, 5+7, welcome_label,
cl0, cl1, cl2, cl3, cl4, cl5, cl6,
struct Component *stack = app_component_new_stack(false, 5+8, welcome_label,
cl0, cl1, cl2, cl3, cl4, cl5, cl6, cl7,
launch_label, label, btn_next, btn_launch);
app->label = label;