Compare commits
2 commits
ed4f5220cd
...
7672ae61d5
| Author | SHA1 | Date | |
|---|---|---|---|
| 7672ae61d5 | |||
| 2e5a9568e7 |
2 changed files with 99 additions and 75 deletions
|
|
@ -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
169
src/app.c
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue