diff --git a/src/userland/Makefile b/src/userland/Makefile index 9b27514..90b586b 100644 --- a/src/userland/Makefile +++ b/src/userland/Makefile @@ -11,7 +11,7 @@ LDFLAGS = -m elf_x86_64 -nostdlib -static -no-pie -Ttext=0x40000000 --no-dynamic BIN_DIR = bin LIBC_SOURCES = $(wildcard libc/*.c) -LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o +LIBC_OBJS = $(patsubst libc/%.c, $(BIN_DIR)/%.o, $(LIBC_SOURCES)) $(BIN_DIR)/crt0.o $(BIN_DIR)/libwidget.o VPATH = cli gui sys games libc net vpath %.c cli gui sys games libc net @@ -34,6 +34,9 @@ $(BIN_DIR)/crt0.o: crt0.asm $(BIN_DIR)/%.o: libc/%.c $(CC) $(CFLAGS) -c $< -o $@ +$(BIN_DIR)/libwidget.o: ../wm/libwidget.c + $(CC) $(CFLAGS) -c $< -o $@ + $(BIN_DIR)/stb_image.o: stb_image.c $(CC) $(CFLAGS) -c $< -o $@ diff --git a/src/userland/gui/boredword.c b/src/userland/gui/boredword.c index a000744..af51ba4 100644 --- a/src/userland/gui/boredword.c +++ b/src/userland/gui/boredword.c @@ -4,6 +4,7 @@ #include "libc/syscall.h" #include "libc/libui.h" #include "libc/stdlib.h" +#include "../../wm/libwidget.h" #include #define COLOR_DARK_PANEL 0xFF202020 @@ -90,8 +91,30 @@ static char open_filename[256] = ""; static _Bool file_modified = 0; static int scroll_y = 0; -static _Bool is_dragging_scrollbar = 0; -static int scrollbar_drag_offset_y = 0; +static widget_scrollbar_t doc_scrollbar; + +static void word_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) { + ui_draw_rect((ui_window_t)user_data, x, y, w, h, color); +} +static void word_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) { + ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color); +} +static void word_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) { + ui_draw_string((ui_window_t)user_data, x, y, str, color); +} + +static widget_context_t word_ctx = { + .user_data = 0, + .draw_rect = word_draw_rect, + .draw_rounded_rect_filled = word_draw_rounded_rect_filled, + .draw_string = word_draw_string, + .mark_dirty = NULL +}; + +static void word_on_scroll(void *user_data, int new_scroll_y) { + (void)user_data; + scroll_y = new_scroll_y; +} static _Bool is_in_selection(int p, int r, int c); @@ -1301,20 +1324,13 @@ static void draw_document(ui_window_t win) { set_active_font(win, 0); int content_h = current_page * (page_h + 20) + page_h + 20; - if (content_h > win_h - 40) { - int sb_x = win_w - 12; - int sb_w = 12; - int sb_h = win_h - 40; - float ratio = (float)(win_h - 40) / (float)content_h; - int thumb_h = (int)(sb_h * ratio); - if (thumb_h < 20) thumb_h = 20; - int max_scroll = content_h - (win_h - 40); - if (scroll_y > max_scroll) scroll_y = max_scroll; - int thumb_y = 40 + (int)(((float)scroll_y / max_scroll) * (sb_h - thumb_h)); - - ui_draw_rect(win, sb_x, 40, sb_w, sb_h, 0xFF303030); - ui_draw_rounded_rect_filled(win, sb_x+2, thumb_y+2, sb_w-4, thumb_h-4, 4, 0xFF606060); - } + doc_scrollbar.x = win_w - 12; + doc_scrollbar.y = 40; + doc_scrollbar.w = 12; + doc_scrollbar.h = win_h - 40; + doc_scrollbar.on_scroll = word_on_scroll; + widget_scrollbar_update(&doc_scrollbar, content_h, scroll_y); + widget_scrollbar_draw(&word_ctx, &doc_scrollbar); } static void ensure_cursor_visible(ui_window_t win) { @@ -1645,32 +1661,8 @@ static void handle_click(ui_window_t win, int x, int y) { } } content_h = dummy_page * (page_h + 20) + page_h + 20; - - int sb_x = win_w - 12; - int sb_w = 12; - int sb_h = win_h - 40; - int thumb_y = 40; - int thumb_h = 0; - int max_scroll = 0; - if (content_h > win_h - 40) { - float ratio = (float)(win_h - 40) / (float)content_h; - thumb_h = (int)(sb_h * ratio); - if (thumb_h < 20) thumb_h = 20; - max_scroll = content_h - (win_h - 40); - if (scroll_y > max_scroll) scroll_y = max_scroll; - thumb_y = 40 + (int)(((float)scroll_y / max_scroll) * (sb_h - thumb_h)); - } - - if (content_h > win_h - 40 && x >= sb_x && x < sb_x + sb_w) { - if (y >= thumb_y && y < thumb_y + thumb_h) { - is_dragging_scrollbar = 1; - scrollbar_drag_offset_y = y - thumb_y; - } else { - if (y < thumb_y) scroll_y -= (win_h - 40); - else scroll_y += (win_h - 40); - if (scroll_y < 0) scroll_y = 0; - if (scroll_y > max_scroll) scroll_y = max_scroll; - } + widget_scrollbar_update(&doc_scrollbar, content_h, scroll_y); + if (widget_scrollbar_handle_mouse(&doc_scrollbar, x, y, true, NULL)) { return; } @@ -1848,12 +1840,15 @@ int main(int argc, char **argv) { (void)argv; ui_window_t win = ui_window_create("BoredWord", 100, 100, win_w, win_h); if (!win) return 1; + word_ctx.user_data = (void*)win; ui_window_set_resizable(win, 1); load_fonts(); set_active_font(win, 0); init_doc(); + widget_scrollbar_init(&doc_scrollbar, win_w - 12, 40, 12, win_h - 40); + if (argc > 1) { load_file(win, argv[1]); } @@ -1892,61 +1887,12 @@ int main(int argc, char **argv) { needs_repaint = 1; } else if (ev.type == GUI_EVENT_MOUSE_UP) { is_dragging = 0; - is_dragging_scrollbar = 0; + widget_scrollbar_handle_mouse(&doc_scrollbar, ev.arg1, ev.arg2, false, NULL); needs_repaint = 1; } else if (ev.type == GUI_EVENT_MOUSE_MOVE) { - if (is_dragging_scrollbar) { - int pw, ph; get_page_size(&pw, &ph); - int doc_view_w = win_w - 40; - float scale = (float)doc_view_w / (float)pw; if (scale > 1.0f) scale = 1.0f; - int page_w = (int)(pw * scale); - int page_h = (int)(ph * scale); - - int content_h = 0; - int dummy_y = 10; int dummy_page = 0; - for(int p=0; prun_count) { - int max_h = 16; int end_run = start_run; int end_char = start_char; int line_w = 0; - int r_idx = start_run; int c_idx = start_char; int last_space_run = -1; int last_space_char = -1; int last_space_w = 0; - while(r_idx < para->run_count) { - TextRun *run = ¶->runs[r_idx]; set_active_font(win, run->font_idx); - int fh = ui_get_font_height_scaled(run->font_size); if (fh > max_h) max_h = fh; - while(c_idx < run->len) { - char buf[2] = {run->text[c_idx], 0}; int cw = ui_get_string_width_scaled(buf, run->font_size); - if (run->text[c_idx] == ' ') { last_space_run = r_idx; last_space_char = c_idx; last_space_w = line_w + cw; } - if (line_w + cw > page_w - 20) break; - line_w += cw; c_idx++; - } - if (c_idx < run->len) break; - r_idx++; c_idx = 0; - } - if (r_idx < para->run_count || (r_idx == para->run_count - 1 && c_idx < para->runs[r_idx].len)) { - if (last_space_run != -1 && (last_space_run > start_run || last_space_char > start_char)) { end_run = last_space_run; end_char = last_space_char; line_w = last_space_w; } - else { end_run = r_idx; end_char = c_idx; } - } else { end_run = para->run_count; end_char = 0; } - int line_h = (int)(max_h * para->spacing) + 4; - if (dummy_y + line_h > dummy_page * (page_h + 20) + page_h - 10) { dummy_page++; dummy_y = dummy_page * (page_h + 20) + 10; } - dummy_y += line_h; start_run = end_run; start_char = end_char; - if (start_run < para->run_count && para->runs[start_run].text[start_char] == ' ') { start_char++; if (start_char >= para->runs[start_run].len) { start_char = 0; start_run++; } } - } - } - content_h = dummy_page * (page_h + 20) + page_h + 20; - - if (content_h > win_h - 40) { - int sb_h = win_h - 40; - float ratio = (float)(win_h - 40) / (float)content_h; - int thumb_h = (int)(sb_h * ratio); - if (thumb_h < 20) thumb_h = 20; - int max_scroll = content_h - (win_h - 40); - - int new_thumb_y = ev.arg2 - scrollbar_drag_offset_y; - if (new_thumb_y < 40) new_thumb_y = 40; - if (new_thumb_y > 40 + sb_h - thumb_h) new_thumb_y = 40 + sb_h - thumb_h; - - scroll_y = (int)(((float)(new_thumb_y - 40) / (sb_h - thumb_h)) * max_scroll); - needs_repaint = 1; - } + if (doc_scrollbar.is_dragging) { + widget_scrollbar_handle_mouse(&doc_scrollbar, ev.arg1, ev.arg2, true, NULL); + needs_repaint = 1; } else if (is_dragging && ev.arg2 >= 40 && active_dialog == 0 && active_dropdown == 0) { handle_click(win, ev.arg1, ev.arg2); needs_repaint = 1; diff --git a/src/userland/gui/browser.c b/src/userland/gui/browser.c index 8bde04b..c28bbbf 100644 --- a/src/userland/gui/browser.c +++ b/src/userland/gui/browser.c @@ -137,6 +137,38 @@ static int scroll_y = 0; static int total_content_height = 0; static int focused_element = -1; +#include "../../wm/libwidget.h" + +static void browser_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) { + ui_draw_rect((ui_window_t)user_data, x, y, w, h, color); +} +static void browser_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) { + ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color); +} +static void browser_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) { + ui_draw_string((ui_window_t)user_data, x, y, str, color); +} +static int browser_measure_string_width(void *user_data, const char *str) { + (void)user_data; + return (int)ui_get_string_width(str); +} +static void browser_mark_dirty(void *user_data, int x, int y, int w, int h) { + ui_mark_dirty((ui_window_t)user_data, x, y, w, h); +} + +static widget_context_t browser_ctx = { + .draw_rect = browser_draw_rect, + .draw_rounded_rect_filled = browser_draw_rounded_rect_filled, + .draw_string = browser_draw_string, + .measure_string_width = browser_measure_string_width, + .mark_dirty = browser_mark_dirty +}; + +static widget_scrollbar_t browser_scrollbar; +static widget_textbox_t url_tb; +static widget_button_t btn_back; +static widget_button_t btn_home; + static void parse_html(const char *html); static void parse_html_incremental(const char *html, int safe_len); static void browser_reflow(void); @@ -1506,6 +1538,7 @@ static void parse_html_incremental(const char *html, int safe_len) { } static void browser_paint(void) { + browser_ctx.user_data = (void *)win_browser; ui_draw_rect(win_browser, 0, 0, win_w, win_h, COLOR_BG); for (int i = 0; i < element_count; i++) { @@ -1520,53 +1553,35 @@ static void browser_paint(void) { if (pixels) ui_draw_image(win_browser, el->x, draw_y, el->img_w, el->img_h, pixels); else ui_draw_rect(win_browser, el->x, draw_y, 100, 80, 0xFFCCCCCC); } else if (el->tag == TAG_INPUT) { - ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFFFFFFFF); - uint32_t border = (focused_element == i) ? 0xFF0000FF : 0xFF808080; - ui_draw_rect(win_browser, el->x, draw_y, el->w, 1, border); - ui_draw_rect(win_browser, el->x, draw_y + el->h - 1, el->w, 1, border); - ui_draw_rect(win_browser, el->x, draw_y, 1, el->h, border); - ui_draw_rect(win_browser, el->x + el->w - 1, draw_y, 1, el->h, border); - - char visible[64]; + char visible[128]; int v_len = 0; int max_v = (el->w - 10) / 8; - if (max_v > 63) max_v = 63; + if (max_v > 127) max_v = 127; for (int k = el->input_scroll; el->attr_value[k] && v_len < max_v; k++) { visible[v_len++] = el->attr_value[k]; } visible[v_len] = 0; - ui_draw_string(win_browser, el->x + 5, draw_y + 2, visible, (focused_element == i) ? 0xFF000000 : 0xFF808080); - - if (focused_element == i) { - int cursor_pos = el->input_cursor - el->input_scroll; - if (cursor_pos >= 0 && cursor_pos < max_v) { - char sub[64]; - int k; - for (k = 0; k < cursor_pos && visible[k]; k++) sub[k] = visible[k]; - sub[k] = 0; - int cx = ui_get_string_width(sub); - ui_draw_rect(win_browser, el->x + 5 + cx, draw_y + 16, 8, 2, 0xFF000000); - } - } + + widget_textbox_t tb; + widget_textbox_init(&tb, el->x, draw_y, el->w, el->h, visible, max_v); + tb.cursor_pos = el->input_cursor - el->input_scroll; + if (tb.cursor_pos < 0) tb.cursor_pos = 0; + tb.focused = (focused_element == i); + widget_textbox_draw(&browser_ctx, &tb); } else if (el->tag == TAG_BUTTON) { - ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFFDDDDDD); - ui_draw_rect(win_browser, el->x, draw_y, el->w, 1, 0xFFFFFFFF); - ui_draw_rect(win_browser, el->x, draw_y + el->h - 1, el->w, 1, 0xFF888888); - ui_draw_rect(win_browser, el->x, draw_y, 1, el->h, 0xFFFFFFFF); - ui_draw_rect(win_browser, el->x + el->w - 1, draw_y, 1, el->h, 0xFF888888); - ui_draw_string(win_browser, el->x + 10, draw_y + 4, el->attr_value, 0xFF000000); + widget_button_t btn; + widget_button_init(&btn, el->x, draw_y, el->w, el->h, el->attr_value); + widget_button_draw(&browser_ctx, &btn); } else if (el->tag == TAG_RADIO) { - ui_draw_rounded_rect_filled(win_browser, el->x, draw_y, el->w, el->h, el->w/2, 0xFF808080); - ui_draw_rounded_rect_filled(win_browser, el->x + 1, draw_y + 1, el->w - 2, el->h - 2, (el->w-2)/2, 0xFFFFFFFF); - if (el->checked) { - ui_draw_rounded_rect_filled(win_browser, el->x + 4, draw_y + 4, el->w - 8, el->h - 8, (el->w-8)/2, 0xFF000000); - } + widget_checkbox_t cb; + widget_checkbox_init(&cb, el->x, draw_y, el->w, el->h, "", true); + cb.checked = el->checked; + widget_checkbox_draw(&browser_ctx, &cb); } else if (el->tag == TAG_CHECKBOX) { - ui_draw_rect(win_browser, el->x, draw_y, el->w, el->h, 0xFF808080); - ui_draw_rect(win_browser, el->x + 1, draw_y + 1, el->w - 2, el->h - 2, 0xFFFFFFFF); - if (el->checked) { - ui_draw_rect(win_browser, el->x + 4, draw_y + 4, el->w - 8, el->h - 8, 0xFF000000); - } + widget_checkbox_t cb; + widget_checkbox_init(&cb, el->x, draw_y, el->w, el->h, "", false); + cb.checked = el->checked; + widget_checkbox_draw(&browser_ctx, &cb); } else if (el->tag == TAG_HR) { ui_draw_rect(win_browser, el->x, draw_y + el->h / 2, el->w, 2, 0xFF888888); ui_draw_rect(win_browser, el->x, draw_y + (el->h / 2) + 2, el->w, 1, 0xFFFFFFFF); @@ -1588,40 +1603,26 @@ static void browser_paint(void) { } ui_draw_rect(win_browser, 0, 0, win_w, URL_BAR_H, COLOR_URL_BAR); - ui_draw_string(win_browser, 10, 8, url_input_buffer, COLOR_URL_TEXT); - if (focused_element == -1) { - char sub[512]; - int k; - for (k = 0; k < url_cursor && url_input_buffer[k]; k++) sub[k] = url_input_buffer[k]; - sub[k] = 0; - int cx = ui_get_string_width(sub); - ui_draw_rect(win_browser, 10 + cx, 22, 8, 2, COLOR_URL_TEXT); - } + + widget_textbox_init(&url_tb, 10, 5, win_w - SCROLL_BAR_W - BTN_W*2 - BTN_PAD*2 - 20, 20, url_input_buffer, 511); + url_tb.cursor_pos = url_cursor; + url_tb.focused = (focused_element == -1); + widget_textbox_draw(&browser_ctx, &url_tb); // Back button int btn_y = (URL_BAR_H - BTN_H) / 2; - uint32_t back_col = history_count > 0 ? 0xFF505050 : 0xFF404040; - ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, BTN_H, back_col); - ui_draw_rect(win_browser, BACK_BTN_X, btn_y, BTN_W, 1, 0xFF606060); - ui_draw_rect(win_browser, BACK_BTN_X, btn_y, 1, BTN_H, 0xFF606060); - ui_draw_rect(win_browser, BACK_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020); - ui_draw_rect(win_browser, BACK_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020); - ui_draw_string(win_browser, BACK_BTN_X + 10, btn_y + 4, "<", history_count > 0 ? 0xFFFFFFFF : 0xFF808080); + widget_button_init(&btn_back, BACK_BTN_X, btn_y, BTN_W, BTN_H, "<"); + widget_button_draw(&browser_ctx, &btn_back); // Home button - ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, BTN_H, 0xFF505050); - ui_draw_rect(win_browser, HOME_BTN_X, btn_y, BTN_W, 1, 0xFF606060); - ui_draw_rect(win_browser, HOME_BTN_X, btn_y, 1, BTN_H, 0xFF606060); - ui_draw_rect(win_browser, HOME_BTN_X, btn_y + BTN_H - 1, BTN_W, 1, 0xFF202020); - ui_draw_rect(win_browser, HOME_BTN_X + BTN_W - 1, btn_y, 1, BTN_H, 0xFF202020); - ui_draw_string(win_browser, HOME_BTN_X + 10, btn_y + 4, "H", 0xFFFFFFFF); + widget_button_init(&btn_home, HOME_BTN_X, btn_y, BTN_W, BTN_H, "H"); + widget_button_draw(&browser_ctx, &btn_home); // Scroll bar - ui_draw_rect(win_browser, win_w - SCROLL_BAR_W, URL_BAR_H, SCROLL_BAR_W, win_h - URL_BAR_H, COLOR_SCROLL_BG); - int thumb_h = (win_h - URL_BAR_H) * (win_h - URL_BAR_H) / (total_content_height > win_h ? total_content_height : win_h); - if (thumb_h < 20) thumb_h = 20; - int thumb_y = URL_BAR_H + (scroll_y * (win_h - URL_BAR_H - thumb_h)) / (total_content_height > win_h - URL_BAR_H ? total_content_height - (win_h - URL_BAR_H) : 1); - ui_draw_rect(win_browser, win_w - SCROLL_BAR_W + 2, thumb_y, SCROLL_BAR_W - 4, thumb_h, COLOR_SCROLL_BTN); + int viewport_h = win_h - URL_BAR_H; + widget_scrollbar_init(&browser_scrollbar, win_w - SCROLL_BAR_W, URL_BAR_H, SCROLL_BAR_W, viewport_h); + widget_scrollbar_update(&browser_scrollbar, total_content_height, scroll_y); + widget_scrollbar_draw(&browser_ctx, &browser_scrollbar); } static void navigate(const char *url) { @@ -1670,34 +1671,52 @@ int main(int argc, char **argv) { continue; } - else if (ev.type == GUI_EVENT_CLICK) { + else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_MOUSE_UP || ev.type == GUI_EVENT_MOUSE_MOVE) { int mx = ev.arg1; - if (mx >= win_w - SCROLL_BAR_W) { - if (ev.arg2 < URL_BAR_H + (win_h - URL_BAR_H)/2) scroll_y -= 100; - else scroll_y += 100; - if (scroll_y < 0) scroll_y = 0; - needs_repaint = true; + int my = ev.arg2; + bool is_down = (ev.type == GUI_EVENT_MOUSE_DOWN || (ev.type == GUI_EVENT_MOUSE_MOVE && browser_scrollbar.is_dragging)); + bool is_click = (ev.type == GUI_EVENT_CLICK); + + if (widget_scrollbar_handle_mouse(&browser_scrollbar, mx, my, is_down, &browser_ctx)) { + if (scroll_y != browser_scrollbar.scroll_y) { + scroll_y = browser_scrollbar.scroll_y; + needs_repaint = true; + } + if (ev.type == GUI_EVENT_MOUSE_MOVE) continue; + if (is_down || is_click) continue; + } + + if (ev.type != GUI_EVENT_CLICK && ev.type != GUI_EVENT_MOUSE_DOWN) continue; + + if (my < URL_BAR_H) { + if (widget_textbox_handle_mouse(&url_tb, mx, my, is_click, NULL)) { + focused_element = -1; + needs_repaint = true; + continue; + } + if (widget_button_handle_mouse(&btn_back, mx, my, is_down, is_click, NULL)) { + if (is_click && history_count > 0) { + history_count--; + int j=0; while(history_stack[history_count][j]) { url_input_buffer[j] = history_stack[history_count][j]; j++; } url_input_buffer[j] = 0; url_cursor = j; + navigate(url_input_buffer); scroll_y = 0; focused_element = -1; + } + needs_repaint = true; continue; + } + if (widget_button_handle_mouse(&btn_home, mx, my, is_down, is_click, NULL)) { + if (is_click) { + if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; } + const char *home = "http://find.boreddev.nl"; + int j=0; while(home[j]) { url_input_buffer[j] = home[j]; j++; } url_input_buffer[j] = 0; url_cursor = j; + navigate(url_input_buffer); scroll_y = 0; focused_element = -1; + } + needs_repaint = true; continue; + } + if (is_click) { + focused_element = -1; needs_repaint = true; + } continue; } - if (ev.arg2 < URL_BAR_H) { - // Check back button - if (mx >= BACK_BTN_X && mx < BACK_BTN_X + BTN_W && history_count > 0) { - history_count--; - int j=0; while(history_stack[history_count][j]) { url_input_buffer[j] = history_stack[history_count][j]; j++; } url_input_buffer[j] = 0; url_cursor = j; - navigate(url_input_buffer); scroll_y = 0; focused_element = -1; - needs_repaint = true; continue; - } - // Check home button - if (mx >= HOME_BTN_X && mx < HOME_BTN_X + BTN_W) { - if (history_count < HISTORY_MAX) { int j=0; while(url_input_buffer[j]) { history_stack[history_count][j] = url_input_buffer[j]; j++; } history_stack[history_count][j] = 0; history_count++; } - const char *home = "http://find.boreddev.nl"; - int j=0; while(home[j]) { url_input_buffer[j] = home[j]; j++; } url_input_buffer[j] = 0; url_cursor = j; - navigate(url_input_buffer); scroll_y = 0; focused_element = -1; - needs_repaint = true; continue; - } - focused_element = -1; needs_repaint = true; continue; - } - int my = ev.arg2 - URL_BAR_H + scroll_y; + my = ev.arg2 - URL_BAR_H + scroll_y; bool found = false; for (int i = 0; i < element_count; i++) { RenderElement *el = &elements[i]; diff --git a/src/userland/gui/calculator.c b/src/userland/gui/calculator.c index 83aa2da..e11fd46 100644 --- a/src/userland/gui/calculator.c +++ b/src/userland/gui/calculator.c @@ -3,6 +3,7 @@ // This header needs to maintain in any file it is present in, as per the GPL license terms. #include "syscall.h" #include "libui.h" +#include "../../wm/libwidget.h" #include #include "stdlib.h" @@ -25,6 +26,33 @@ static long long calc_decimal_divisor = 10; static char display_buffer[1024]; static int display_buf_len = 0; +static widget_button_t buttons[20]; +static const char *labels[] = { + "C", "sqr", "rt", "/", + "7", "8", "9", "*", + "4", "5", "6", "-", + "1", "2", "3", "+", + "0", ".", "BS", "=" +}; + +static void calc_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) { + ui_draw_rect((ui_window_t)user_data, x, y, w, h, color); +} +static void calc_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) { + ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color); +} +static void calc_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) { + ui_draw_string((ui_window_t)user_data, x, y, str, color); +} + +static widget_context_t calc_ctx = { + .user_data = 0, + .draw_rect = calc_draw_rect, + .draw_rounded_rect_filled = calc_draw_rounded_rect_filled, + .draw_string = calc_draw_string, + .mark_dirty = NULL +}; + static long long isqrt(long long n) { if (n < 0) return -1; if (n == 0) return 0; @@ -97,27 +125,8 @@ static void calculator_paint(void) { int text_x = w - 15 - text_w; ui_draw_string(win_calculator, text_x, 18, display_buffer, COLOR_DARK_TEXT); - const char *labels[] = { - "C", "sqr", "rt", "/", - "7", "8", "9", "*", - "4", "5", "6", "-", - "1", "2", "3", "+", - "0", ".", "BS", "=" - }; - - int bw = 35; - int bh = 25; - int gap = 5; - int start_x = 10; - int start_y = 40; - for (int i = 0; i < 20; i++) { - int r = i / 4; - int c = i % 4; - ui_draw_rounded_rect_filled(win_calculator, start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, 4, COLOR_DARK_BORDER); - int label_x = start_x + c*(bw+gap) + 5; - int label_y = start_y + r*(bh+gap) + 9; - ui_draw_string(win_calculator, label_x, label_y, labels[i], COLOR_DARK_TEXT); + widget_button_draw(&calc_ctx, &buttons[i]); } } @@ -135,105 +144,93 @@ static void do_op(void) { } } -static void calculator_click(int x, int y) { - int bw = 35; - int bh = 25; - int gap = 5; - int start_x = 10; - int start_y = 35; // Matches the hitboxes +static void handle_button_click(int idx) { + const char *labels_map[] = { + "C", "s", "r", "/", + "7", "8", "9", "*", + "4", "5", "6", "-", + "1", "2", "3", "+", + "0", ".", "B", "=" + }; + char lbl = labels_map[idx][0]; - for (int i = 0; i < 20; i++) { - int r = i / 4; - int c = i % 4; - int bx = start_x + c*(bw+gap); - int by = start_y + r*(bh+gap); - - if (x >= bx && x < bx + bw && y >= by && y < by + bh) { - const char *labels[] = { - "C", "s", "r", "/", - "7", "8", "9", "*", - "4", "5", "6", "-", - "1", "2", "3", "+", - "0", ".", "B", "=" - }; - char lbl = labels[i][0]; - - if (lbl >= '0' && lbl <= '9') { - if (calc_new_entry || calc_error) { - calc_curr = (lbl - '0') * SCALE; - calc_new_entry = false; - calc_decimal_mode = false; - } else { - if (calc_decimal_mode) { - if (calc_decimal_divisor <= SCALE) { - long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor; - if (calc_curr >= 0) calc_curr += digit_val; - else calc_curr -= digit_val; - calc_decimal_divisor *= 10; - } - } else { - if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE; - else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE; - } - } - calc_error = false; - } else if (lbl == '.') { - if (calc_new_entry) { - calc_curr = 0; - calc_new_entry = false; - } - if (!calc_decimal_mode) { - calc_decimal_mode = true; - calc_decimal_divisor = 10; - } - } else if (lbl == 'C') { - calc_curr = 0; calc_acc = 0; calc_op = 0; - calc_new_entry = true; calc_error = false; calc_decimal_mode = false; - } else if (lbl == 'B') { - if (!calc_new_entry && !calc_error) { - if (calc_decimal_mode) { - if (calc_decimal_divisor > 10) { - calc_decimal_divisor /= 10; - long long unit = SCALE / calc_decimal_divisor; - calc_curr = (calc_curr / (unit * 10)) * (unit * 10); - } else { - calc_decimal_mode = false; - calc_decimal_divisor = 10; - calc_curr = (calc_curr / SCALE) * SCALE; - } - } else { - calc_curr = (calc_curr / SCALE / 10) * SCALE; - } - } - } else if (lbl == 's') { // sqr - calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true; - } else if (lbl == 'r') { // rt - long long s = isqrt(calc_curr); - if (s == -1) calc_error = true; - else calc_curr = s * 1000; - calc_new_entry = true; - } else if (lbl == '=') { - do_op(); - calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false; + if (lbl >= '0' && lbl <= '9') { + if (calc_new_entry || calc_error) { + calc_curr = (lbl - '0') * SCALE; + calc_new_entry = false; + calc_decimal_mode = false; + } else { + if (calc_decimal_mode) { + if (calc_decimal_divisor <= SCALE) { + long long digit_val = ((long long)(lbl - '0') * SCALE) / calc_decimal_divisor; + if (calc_curr >= 0) calc_curr += digit_val; + else calc_curr -= digit_val; + calc_decimal_divisor *= 10; + } } else { - if (!calc_new_entry) { - if (calc_op) do_op(); - else calc_acc = calc_curr; - } - calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false; + if (calc_curr >= 0) calc_curr = calc_curr * 10 + (lbl - '0') * SCALE; + else calc_curr = calc_curr * 10 - (lbl - '0') * SCALE; } - - update_display(); - calculator_paint(); - ui_mark_dirty(win_calculator, 0, 0, 180, 230); - return; } + calc_error = false; + } else if (lbl == '.') { + if (calc_new_entry) { + calc_curr = 0; + calc_new_entry = false; + } + if (!calc_decimal_mode) { + calc_decimal_mode = true; + calc_decimal_divisor = 10; + } + } else if (lbl == 'C') { + calc_curr = 0; calc_acc = 0; calc_op = 0; + calc_new_entry = true; calc_error = false; calc_decimal_mode = false; + } else if (lbl == 'B') { + if (!calc_new_entry && !calc_error) { + if (calc_decimal_mode) { + if (calc_decimal_divisor > 10) { + calc_decimal_divisor /= 10; + long long unit = SCALE / calc_decimal_divisor; + calc_curr = (calc_curr / (unit * 10)) * (unit * 10); + } else { + calc_decimal_mode = false; + calc_decimal_divisor = 10; + calc_curr = (calc_curr / SCALE) * SCALE; + } + } else { + calc_curr = (calc_curr / SCALE / 10) * SCALE; + } + } + } else if (lbl == 's') { // sqr + calc_curr = (calc_curr * calc_curr) / SCALE; calc_new_entry = true; + } else if (lbl == 'r') { // rt + long long s = isqrt(calc_curr); + if (s == -1) calc_error = true; + else calc_curr = s * 1000; + calc_new_entry = true; + } else if (lbl == '=') { + do_op(); + calc_curr = calc_acc; calc_op = 0; calc_new_entry = true; calc_decimal_mode = false; + } else { + if (!calc_new_entry) { + if (calc_op) do_op(); + else calc_acc = calc_curr; + } + calc_op = lbl; calc_new_entry = true; calc_decimal_mode = false; } } int main(void) { win_calculator = ui_window_create("Calculator", 200, 200, 180, 230); + calc_ctx.user_data = (void *)win_calculator; + int bw = 35, bh = 25, gap = 5, start_x = 10, start_y = 40; + for (int i = 0; i < 20; i++) { + int r = i / 4; + int c = i % 4; + widget_button_init(&buttons[i], start_x + c*(bw+gap), start_y + r*(bh+gap), bw, bh, labels[i]); + } + calc_curr = 0; calc_acc = 0; calc_op = 0; @@ -249,8 +246,21 @@ int main(void) { if (ev.type == GUI_EVENT_PAINT) { calculator_paint(); ui_mark_dirty(win_calculator, 0, 0, 180, 230); - } else if (ev.type == GUI_EVENT_CLICK) { - calculator_click(ev.arg1, ev.arg2); + } else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN || ev.type == GUI_EVENT_MOUSE_UP) { + bool needs_paint = false; + for (int i=0; i<20; i++) { + if (widget_button_handle_mouse(&buttons[i], ev.arg1, ev.arg2, ev.type == GUI_EVENT_MOUSE_DOWN, ev.type == GUI_EVENT_CLICK, NULL)) { + needs_paint = true; + if (ev.type == GUI_EVENT_CLICK) { + handle_button_click(i); + update_display(); + } + } + } + if (needs_paint) { + calculator_paint(); + ui_mark_dirty(win_calculator, 0, 0, 180, 230); + } } else if (ev.type == GUI_EVENT_CLOSE) { sys_exit(0); } diff --git a/src/userland/gui/settings.c b/src/userland/gui/settings.c index e4817e5..79415df 100644 --- a/src/userland/gui/settings.c +++ b/src/userland/gui/settings.c @@ -7,6 +7,7 @@ #include "stb_image.h" #include #include +#include "../../wm/libwidget.h" #define COLOR_COFFEE 0xFF6B4423 #define COLOR_TEAL 0xFF008080 @@ -16,6 +17,68 @@ #define COLOR_GREY 0xFF454545 #define COLOR_BLACK 0xFF000000 +#define COLOR_DARK_PANEL 0xFF2D2D2D +#define COLOR_DARK_TEXT 0xFFE0E0E0 + +static void settings_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) { + ui_draw_rect((ui_window_t)user_data, x, y, w, h, color); +} + +static void settings_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) { + ui_draw_rounded_rect_filled((ui_window_t)user_data, x, y, w, h, r, color); +} + +static void settings_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) { + ui_draw_string((ui_window_t)user_data, x, y, str, color); +} + +static int settings_measure_string_width(void *user_data, const char *str) { + (void)user_data; + return (int)ui_get_string_width(str); +} + +static void settings_mark_dirty(void *user_data, int x, int y, int w, int h) { + ui_mark_dirty((ui_window_t)user_data, x, y, w, h); +} + +static widget_context_t settings_ctx = { + .draw_rect = settings_draw_rect, + .draw_rounded_rect_filled = settings_draw_rounded_rect_filled, + .draw_string = settings_draw_string, + .measure_string_width = settings_measure_string_width, + .mark_dirty = settings_mark_dirty +}; + +static widget_checkbox_t chk_snap; +static widget_checkbox_t chk_align; +static widget_dropdown_t drop_res; +static widget_dropdown_t drop_color; +static widget_textbox_t tb_r, tb_g, tb_b; +static widget_textbox_t tb_ip, tb_dns; +static widget_button_t btn_apply, btn_back; + +#define MAX_WALLPAPERS 10 +#define MAX_FONTS 16 + +static widget_button_t btn_main_wallpaper, btn_main_network, btn_main_desktop, btn_main_mouse, btn_main_fonts, btn_main_display; +static widget_button_t btn_wp_colors[6]; +static widget_button_t btn_wp_patterns[2]; +static widget_button_t btn_wp_apply; +static widget_button_t btn_wp_thumbs[MAX_WALLPAPERS]; + +static widget_button_t btn_net_init; +static widget_button_t btn_net_set_ip, btn_net_set_dns; + +static widget_button_t btn_dt_cols_minus, btn_dt_cols_plus; +static widget_button_t btn_dt_rows_minus, btn_dt_rows_plus; + +static widget_button_t btn_fonts[MAX_FONTS]; +static widget_textbox_t tb_custom_w, tb_custom_h; + +#define COLOR_PURPLE 0xFF800080 +#define COLOR_GREY 0xFF454545 +#define COLOR_BLACK 0xFF000000 + #define COLOR_DARK_PANEL 0xFF2D2D2D #define COLOR_DARK_TEXT 0xFFE0E0E0 #define COLOR_DARK_BORDER 0xFF404040 @@ -79,7 +142,6 @@ static char net_status[64] = ""; static uint32_t pattern_lumberjack[PATTERN_SIZE * PATTERN_SIZE]; static uint32_t pattern_blue_diamond[PATTERN_SIZE * PATTERN_SIZE]; -#define MAX_WALLPAPERS 10 #define WALLPAPER_THUMB_W 80 #define WALLPAPER_THUMB_H 50 @@ -100,7 +162,6 @@ static int desktop_max_cols = 10; static int mouse_speed = 10; // Font selection -#define MAX_FONTS 16 typedef struct { char path[128]; char name[48]; @@ -264,7 +325,7 @@ static void control_panel_paint_main(ui_window_t win) { int item_spacing = 10; // Wallpaper - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_wallpaper); ui_draw_rect(win, offset_x + 12, offset_y + item_y + 8, 40, 40, 0xFF87CEEB); ui_draw_rect(win, offset_x + 12, offset_y + item_y + 28, 40, 20, 0xFF90EE90); ui_draw_rect(win, offset_x + 24, offset_y + item_y + 22, 3, 6, 0xFF654321); @@ -274,7 +335,7 @@ static void control_panel_paint_main(ui_window_t win) { // Network item_y += item_h + item_spacing; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_network); ui_draw_rect(win, offset_x + 18, offset_y + item_y + 12, 24, 24, 0xFF4169E1); ui_draw_rect(win, offset_x + 22, offset_y + item_y + 16, 16, 16, 0xFF87CEEB); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Network", COLOR_DARK_TEXT); @@ -282,7 +343,7 @@ static void control_panel_paint_main(ui_window_t win) { // Desktop item_y += item_h + item_spacing; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_desktop); ui_draw_rect(win, offset_x + 12, offset_y + item_y + 10, 36, 8, 0xFFE0C060); ui_draw_rect(win, offset_x + 12, offset_y + item_y + 18, 36, 22, 0xFFD4A574); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Desktop", COLOR_DARK_TEXT); @@ -290,7 +351,7 @@ static void control_panel_paint_main(ui_window_t win) { // Mouse item_y += item_h + item_spacing; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_mouse); ui_draw_rect(win, offset_x + 18, offset_y + item_y + 8, 20, 28, 0xFFD3D3D3); ui_draw_rect(win, offset_x + 20, offset_y + item_y + 10, 16, 10, 0xFFB0B0B0); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Mouse", COLOR_DARK_TEXT); @@ -298,7 +359,7 @@ static void control_panel_paint_main(ui_window_t win) { // Fonts item_y += item_h + item_spacing; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_fonts); // Font icon: "Aa" stylized ui_draw_string(win, offset_x + 14, offset_y + item_y + 10, "Aa", 0xFF6A9EF5); ui_draw_string(win, offset_x + 60, offset_y + item_y + 15, "Fonts", COLOR_DARK_TEXT); @@ -306,7 +367,7 @@ static void control_panel_paint_main(ui_window_t win) { // Display item_y += item_h + item_spacing; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + item_y, win_w - 16, item_h, 8, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_main_display); // Monitor icon ui_draw_rect(win, offset_x + 14, offset_y + item_y + 12, 32, 22, 0xFF4A90E2); ui_draw_rect(win, offset_x + 16, offset_y + item_y + 14, 28, 18, 0xFF87CEEB); @@ -320,8 +381,7 @@ static void control_panel_paint_wallpaper(ui_window_t win) { int offset_x = 8; int offset_y = 6; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "Presets:", COLOR_DARK_TEXT); @@ -329,28 +389,28 @@ static void control_panel_paint_wallpaper(ui_window_t win) { int button_x = offset_x; // Colors - ui_draw_rounded_rect_filled(win, button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[0]); ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_COFFEE); ui_draw_string(win, button_x + 35, button_y + 8, "Coffee", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[1]); ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_TEAL); ui_draw_string(win, button_x + 135, button_y + 8, "Teal", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[2]); ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREEN); ui_draw_string(win, button_x + 235, button_y + 8, "Green", COLOR_DARK_TEXT); button_y += 35; - ui_draw_rounded_rect_filled(win, button_x, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[3]); ui_draw_rect(win, button_x + 8, button_y + 6, 18, 13, COLOR_BLUE_BG); ui_draw_string(win, button_x + 35, button_y + 8, "Blue", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 100, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[4]); ui_draw_rect(win, button_x + 108, button_y + 6, 18, 13, COLOR_PURPLE); ui_draw_string(win, button_x + 132, button_y + 8, "Purple", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 200, button_y, 91, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_colors[5]); ui_draw_rect(win, button_x + 208, button_y + 6, 18, 13, COLOR_GREY); ui_draw_string(win, button_x + 235, button_y + 8, "Grey", COLOR_DARK_TEXT); @@ -359,7 +419,7 @@ static void control_panel_paint_wallpaper(ui_window_t win) { ui_draw_string(win, offset_x, button_y, "Patterns:", COLOR_DARK_TEXT); button_y += 20; - ui_draw_rounded_rect_filled(win, button_x, button_y, 132, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_patterns[0]); for (int py = 0; py < 10; py++) { for (int px = 0; px < 12; px++) { int cell_x = px % 3; @@ -371,7 +431,7 @@ static void control_panel_paint_wallpaper(ui_window_t win) { } ui_draw_string(win, button_x + 28, button_y + 8, "Lumberjack", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 145, button_y, 132, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_patterns[1]); for (int py = 0; py < 8; py++) { for (int px = 0; px < 10; px++) { int cx = px - 5; @@ -390,30 +450,18 @@ static void control_panel_paint_wallpaper(ui_window_t win) { button_y += 20; ui_draw_string(win, button_x, button_y + 4, "R:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 25, button_y, 50, 18, 4, COLOR_DARK_PANEL); - ui_draw_string(win, button_x + 30, button_y + 4, rgb_r, (focused_field == 0) ? 0xFFFF6B6B : COLOR_DARK_TEXT); - if (focused_field == 0) { - int cursor_x = button_x + 30 + input_cursor * 8; - ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFFFF6B6B); - } + tb_r.focused = (focused_field == 0); + widget_textbox_draw(&settings_ctx, &tb_r); ui_draw_string(win, button_x + 90, button_y + 4, "G:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 115, button_y, 50, 18, 4, COLOR_DARK_PANEL); - ui_draw_string(win, button_x + 120, button_y + 4, rgb_g, (focused_field == 1) ? 0xFF90EE90 : COLOR_DARK_TEXT); - if (focused_field == 1) { - int cursor_x = button_x + 120 + input_cursor * 8; - ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFF90EE90); - } + tb_g.focused = (focused_field == 1); + widget_textbox_draw(&settings_ctx, &tb_g); ui_draw_string(win, button_x + 180, button_y + 4, "B:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, button_x + 205, button_y, 50, 18, 4, COLOR_DARK_PANEL); - ui_draw_string(win, button_x + 210, button_y + 4, rgb_b, (focused_field == 2) ? 0xFF87CEEB : COLOR_DARK_TEXT); - if (focused_field == 2) { - int cursor_x = button_x + 210 + input_cursor * 8; - ui_draw_rect(win, cursor_x, button_y + 4, 1, 9, 0xFF87CEEB); - } + tb_b.focused = (focused_field == 2); + widget_textbox_draw(&settings_ctx, &tb_b); - ui_draw_rounded_rect_filled(win, button_x, button_y + 25, 70, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_apply); ui_draw_string(win, button_x + 18, button_y + 33, "Apply", COLOR_DARK_TEXT); // Wallpapers section @@ -425,7 +473,7 @@ static void control_panel_paint_wallpaper(ui_window_t win) { int tx = (i % 3) * (WALLPAPER_THUMB_W + 15); int ty = (i / 3) * (WALLPAPER_THUMB_H + 25); - ui_draw_rounded_rect_filled(win, button_x + tx, button_y + ty, WALLPAPER_THUMB_W + 8, WALLPAPER_THUMB_H + 20, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_wp_thumbs[i]); if (wallpapers[i].valid) { for (int py = 0; py < WALLPAPER_THUMB_H; py++) { for (int px = 0; px < WALLPAPER_THUMB_W; px++) { @@ -441,11 +489,10 @@ static void control_panel_paint_network(ui_window_t win) { int offset_x = 8; int offset_y = 6; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "Network Adapter:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 55, 140, 25, 6, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_net_init); ui_draw_string(win, offset_x + 30, offset_y + 63, "Init Network", COLOR_DARK_TEXT); if (net_status[0] != '\0') { @@ -524,28 +571,20 @@ static void control_panel_paint_network(ui_window_t win) { // IP SET ui_draw_string(win, offset_x, info_y + 4, "IPSET:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 60, info_y, 140, 20, 4, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 65, info_y + 4, net_ip, (focused_field == 5) ? 0xFF90EE90 : COLOR_DARK_TEXT); - if (focused_field == 5) { - int cursor_x = offset_x + 65 + input_cursor * 8; - ui_draw_rect(win, cursor_x, info_y + 4, 1, 10, 0xFF90EE90); - } + tb_ip.focused = (focused_field == 5); + widget_textbox_draw(&settings_ctx, &tb_ip); - ui_draw_rounded_rect_filled(win, offset_x + 210, info_y, 50, 20, 4, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_net_set_ip); ui_draw_string(win, offset_x + 225, info_y + 4, "SET", COLOR_DARK_TEXT); info_y += 30; // DNS SET ui_draw_string(win, offset_x, info_y + 4, "DNSSET:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 60, info_y, 140, 20, 4, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 65, info_y + 4, net_dns, (focused_field == 6) ? 0xFF87CEEB : COLOR_DARK_TEXT); - if (focused_field == 6) { - int cursor_x = offset_x + 65 + input_cursor * 8; - ui_draw_rect(win, cursor_x, info_y + 4, 1, 10, 0xFF87CEEB); - } + tb_dns.focused = (focused_field == 6); + widget_textbox_draw(&settings_ctx, &tb_dns); - ui_draw_rounded_rect_filled(win, offset_x + 210, info_y, 50, 20, 4, COLOR_DARK_PANEL); + widget_button_draw(&settings_ctx, &btn_net_set_dns); ui_draw_string(win, offset_x + 225, info_y + 4, "SET", COLOR_DARK_TEXT); } @@ -553,43 +592,44 @@ static void control_panel_paint_desktop(ui_window_t win) { int offset_x = 8; int offset_y = 6; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "Desktop Settings:", COLOR_DARK_TEXT); int section_y = offset_y + 65; - ui_draw_rounded_rect_filled(win, offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); - if (desktop_snap_to_grid) ui_draw_string(win, offset_x + 3, section_y + 1, "v", 0xFF90EE90); - ui_draw_string(win, offset_x + 25, section_y + 3, "Snap to Grid", COLOR_DARK_TEXT); + widget_checkbox_draw(&settings_ctx, &chk_snap); section_y += 25; - ui_draw_rounded_rect_filled(win, offset_x, section_y, 16, 16, 3, COLOR_DARK_PANEL); - if (desktop_auto_align) ui_draw_string(win, offset_x + 3, section_y + 1, "v", 0xFF90EE90); - ui_draw_string(win, offset_x + 25, section_y + 3, "Auto Align Icons", COLOR_DARK_TEXT); + widget_checkbox_draw(&settings_ctx, &chk_align); section_y += 30; ui_draw_string(win, offset_x, section_y + 3, "Apps per column:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); + + widget_button_init(&btn_dt_rows_minus, offset_x + 130, section_y, 20, 20, ""); + widget_button_draw(&settings_ctx, &btn_dt_rows_minus); ui_draw_string(win, offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); char num[4]; cli_itoa(desktop_max_rows_per_col, num); ui_draw_string(win, offset_x + 160, section_y + 5, num, COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); + widget_button_init(&btn_dt_rows_plus, offset_x + 180, section_y, 20, 20, ""); + widget_button_draw(&settings_ctx, &btn_dt_rows_plus); ui_draw_string(win, offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); section_y += 30; ui_draw_string(win, offset_x, section_y + 3, "Columns:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 130, section_y, 20, 20, 4, COLOR_DARK_PANEL); + + widget_button_init(&btn_dt_cols_minus, offset_x + 130, section_y, 20, 20, ""); + widget_button_draw(&settings_ctx, &btn_dt_cols_minus); ui_draw_string(win, offset_x + 135, section_y + 4, "-", COLOR_DARK_TEXT); char num_c[4]; cli_itoa(desktop_max_cols, num_c); ui_draw_string(win, offset_x + 160, section_y + 5, num_c, COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 180, section_y, 20, 20, 4, COLOR_DARK_PANEL); + widget_button_init(&btn_dt_cols_plus, offset_x + 180, section_y, 20, 20, ""); + widget_button_draw(&settings_ctx, &btn_dt_cols_plus); ui_draw_string(win, offset_x + 186, section_y + 4, "+", COLOR_DARK_TEXT); } @@ -597,8 +637,7 @@ static void control_panel_paint_mouse(ui_window_t win) { int offset_x = 8; int offset_y = 6; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "Mouse Settings:", COLOR_DARK_TEXT); int section_y = offset_y + 65; @@ -641,6 +680,9 @@ static void load_fonts(void) { // Store display name (strip .ttf) for (int j = 0; j < nl - 4 && j < 47; j++) fe->name[j] = info[i].name[j]; fe->name[(nl-4 < 47) ? nl-4 : 47] = 0; + + widget_button_init(&btn_fonts[font_count], 8, 66 + (font_count * 40), 330, 35, ""); + font_count++; } } @@ -649,15 +691,13 @@ static void control_panel_paint_fonts(ui_window_t win) { int offset_x = 8; int offset_y = 6; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "System Font:", COLOR_DARK_TEXT); int item_y = offset_y + 60; for (int i = 0; i < font_count; i++) { - uint32_t bg_color = (i == selected_font) ? 0xFF3D5A80 : COLOR_DARK_PANEL; - ui_draw_rounded_rect_filled(win, offset_x, item_y, 330, 35, 6, bg_color); + widget_button_draw(&settings_ctx, &btn_fonts[i]); // Font icon ui_draw_string(win, offset_x + 10, item_y + 9, "Aa", 0xFF6A9EF5); // Font name @@ -674,71 +714,36 @@ static void control_panel_paint_display(ui_window_t win) { int offset_y = 6; int right_x = offset_x + 160; - ui_draw_rounded_rect_filled(win, offset_x, offset_y + 5, 80, 25, 6, COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 10, offset_y + 13, "< Back", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_back); ui_draw_string(win, offset_x, offset_y + 40, "Resolution:", COLOR_DARK_TEXT); - int btn_y = offset_y + 60; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 0) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, "640x480", COLOR_DARK_TEXT); + int custom_y = offset_y + 270; + ui_draw_string(win, offset_x, custom_y - 20, "Custom:", COLOR_DARK_TEXT); - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 1) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, "800x600", COLOR_DARK_TEXT); + widget_textbox_init(&tb_custom_w, offset_x, custom_y, 60, 25, custom_res_w, 5); + tb_custom_w.focused = (focused_field == 3); + widget_textbox_draw(&settings_ctx, &tb_custom_w); - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 2) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[0], COLOR_DARK_TEXT); + ui_draw_string(win, offset_x + 65, custom_y + 7, "x", COLOR_DARK_TEXT); - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 3) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[1], COLOR_DARK_TEXT); - - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 4) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, dyn_res_str[2], COLOR_DARK_TEXT); + widget_textbox_init(&tb_custom_h, offset_x + 80, custom_y, 60, 25, custom_res_h, 5); + tb_custom_h.focused = (focused_field == 4); + widget_textbox_draw(&settings_ctx, &tb_custom_h); - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 140, 30, 6, (disp_sel_res == 5) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 30, btn_y + 10, "Custom:", COLOR_DARK_TEXT); - - btn_y += 35; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 60, 25, 4, (focused_field == 3) ? 0xFF4A90E2 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 5, btn_y + 7, custom_res_w[0] ? custom_res_w : "W", (custom_res_w[0] || focused_field == 3) ? 0xFFFFFFFF : 0xFF888888); - ui_draw_string(win, offset_x + 65, btn_y + 7, "x", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, offset_x + 80, btn_y, 60, 25, 4, (focused_field == 4) ? 0xFF4A90E2 : COLOR_DARK_PANEL); - ui_draw_string(win, offset_x + 85, btn_y + 7, custom_res_h[0] ? custom_res_h : "H", (custom_res_h[0] || focused_field == 4) ? 0xFFFFFFFF : 0xFF888888); - - btn_y = offset_y + 60; ui_draw_string(win, right_x, offset_y + 40, "Color Depth:", COLOR_DARK_TEXT); - ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 0) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, right_x + 40, btn_y + 10, "32-bit", COLOR_DARK_TEXT); + widget_button_draw(&settings_ctx, &btn_apply); - btn_y += 35; - ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 1) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, right_x + 40, btn_y + 10, "16-bit", COLOR_DARK_TEXT); - - btn_y += 35; - ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 2) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, right_x + 25, btn_y + 10, "256 Colors", COLOR_DARK_TEXT); - - btn_y += 35; - ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 3) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, right_x + 30, btn_y + 10, "Grayscale", COLOR_DARK_TEXT); - - btn_y += 35; - ui_draw_rounded_rect_filled(win, right_x, btn_y, 140, 30, 6, (disp_sel_color == 4) ? 0xFF3D5A80 : COLOR_DARK_PANEL); - ui_draw_string(win, right_x + 25, btn_y + 10, "Monochrome", COLOR_DARK_TEXT); - - btn_y = offset_y + 320; - ui_draw_rounded_rect_filled(win, offset_x, btn_y, 300, 35, 6, 0xFF4A90E2); - ui_draw_string(win, offset_x + 125, btn_y + 12, "Apply", 0xFFFFFFFF); + // Draw dropdowns last so they render above everything else + widget_dropdown_draw(&settings_ctx, &drop_res); + widget_dropdown_draw(&settings_ctx, &drop_color); } static void control_panel_paint(ui_window_t win) { // Fill background ui_draw_rect(win, 0, 0, 350, 500, COLOR_DARK_BG); + + settings_ctx.user_data = (void *)win; if (current_view == VIEW_MAIN) { control_panel_paint_main(win); @@ -822,331 +827,223 @@ static void fetch_kernel_state(void) { load_wallpapers(); } -static void control_panel_handle_click(int x, int y) { +static void control_panel_handle_mouse(int x, int y, bool is_down, bool is_click) { int win_w = 350; + if (current_view != VIEW_MAIN && widget_button_handle_mouse(&btn_back, x, y, is_down, is_click, NULL)) { + if (is_click) { + current_view = VIEW_MAIN; + focused_field = -1; + btn_back.pressed = false; + } + return; + } + + if (current_view == VIEW_DESKTOP) { + if (widget_checkbox_handle_mouse(&chk_snap, x, y, is_click, NULL)) { + if (is_click) desktop_snap_to_grid = chk_snap.checked; + return; + } + if (widget_checkbox_handle_mouse(&chk_align, x, y, is_click, NULL)) { + if (is_click) desktop_auto_align = chk_align.checked; + return; + } + } + + if (current_view == VIEW_DISPLAY) { + if (widget_dropdown_handle_mouse(&drop_res, x, y, is_click, NULL)) { + disp_sel_res = drop_res.selected_idx; + return; + } + if (widget_dropdown_handle_mouse(&drop_color, x, y, is_click, NULL)) { + disp_sel_color = drop_color.selected_idx; + return; + } + if (drop_res.is_open || drop_color.is_open) return; + + if (widget_button_handle_mouse(&btn_apply, x, y, is_down, is_click, NULL)) { + if (is_click) { + btn_apply.pressed = false; + int w = 1024, h = 768; + if (disp_sel_res == 0) { w = 640; h = 480; } + else if (disp_sel_res == 1) { w = 800; h = 600; } + else if (disp_sel_res >= 2 && disp_sel_res <= 4) { + w = dyn_res_w[disp_sel_res - 2]; + h = dyn_res_h[disp_sel_res - 2]; + } else if (disp_sel_res == 5) { + extern int atoi(const char *str); + int cw = atoi(custom_res_w); + int ch = atoi(custom_res_h); + if (cw >= 320 && ch >= 200) { w = cw; h = ch; } + } + + int bpp = 32, mode = 0; + if (disp_sel_color == 1) { bpp = 16; } + if (disp_sel_color == 2) { bpp = 8; mode = 0; } + if (disp_sel_color == 3) { bpp = 8; mode = 1; } + if (disp_sel_color == 4) { bpp = 8; mode = 2; } + + sys_system(47 /*SET_RESOLUTION*/, w, h, bpp, mode); + } + return; + } + } + + if (current_view == VIEW_WALLPAPER) { + if (widget_textbox_handle_mouse(&tb_r, x, y, is_click, NULL)) { + focused_field = 0; input_cursor = tb_r.cursor_pos; return; + } + if (widget_textbox_handle_mouse(&tb_g, x, y, is_click, NULL)) { + focused_field = 1; input_cursor = tb_g.cursor_pos; return; + } + if (widget_textbox_handle_mouse(&tb_b, x, y, is_click, NULL)) { + focused_field = 2; input_cursor = tb_b.cursor_pos; return; + } + + for (int i=0; i<6; i++) { + if (widget_button_handle_mouse(&btn_wp_colors[i], x, y, is_down, is_click, NULL)) { + if (is_click) { + uint32_t c = 0; + if (i==0) c = COLOR_COFFEE; else if(i==1) c = COLOR_TEAL; else if(i==2) c = COLOR_GREEN; + else if(i==3) c = COLOR_BLUE_BG; else if(i==4) c = COLOR_PURPLE; else if(i==5) c = COLOR_GREY; + sys_system(1, c, 0, 0, 0); btn_wp_colors[i].pressed=false; + } + return; + } + } + if (widget_button_handle_mouse(&btn_wp_patterns[0], x, y, is_down, is_click, NULL)) { + if (is_click) { sys_system(2, (uint64_t)pattern_lumberjack, 0, 0, 0); btn_wp_patterns[0].pressed=false;} return; + } + if (widget_button_handle_mouse(&btn_wp_patterns[1], x, y, is_down, is_click, NULL)) { + if (is_click) { sys_system(2, (uint64_t)pattern_blue_diamond, 0, 0, 0); btn_wp_patterns[1].pressed=false;} return; + } + if (widget_button_handle_mouse(&btn_wp_apply, x, y, is_down, is_click, NULL)) { + if (is_click) { + uint32_t cust = parse_rgb_separate(rgb_r, rgb_g, rgb_b); + sys_system(1, cust, 0, 0, 0); + btn_wp_apply.pressed=false; + } + return; + } + for (int i=0; i 1) { desktop_max_rows_per_col--; save_desktop_config(); } btn_dt_rows_minus.pressed=false; } return; + } + if (widget_button_handle_mouse(&btn_dt_rows_plus, x, y, is_down, is_click, NULL)) { + if (is_click) { if (desktop_max_rows_per_col < 15) desktop_max_rows_per_col++; save_desktop_config(); btn_dt_rows_plus.pressed=false;} return; + } + if (widget_button_handle_mouse(&btn_dt_cols_minus, x, y, is_down, is_click, NULL)) { + if (is_click) { if (desktop_max_cols > 1) { desktop_max_cols--; save_desktop_config(); } btn_dt_cols_minus.pressed=false;} return; + } + if (widget_button_handle_mouse(&btn_dt_cols_plus, x, y, is_down, is_click, NULL)) { + if (is_click) { desktop_max_cols++; save_desktop_config(); btn_dt_cols_plus.pressed=false;} return; + } + } + + if (current_view == VIEW_FONTS) { + for (int i=0; i= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_WALLPAPER; - focused_field = -1; - } - item_y += item_h + item_spacing; - if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_NETWORK; - focused_field = -1; - } - item_y += item_h + item_spacing; - if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_DESKTOP; - } - item_y += item_h + item_spacing; - if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_MOUSE; - } - item_y += item_h + item_spacing; - if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_FONTS; - if (font_count == 0) load_fonts(); - } - item_y += item_h + item_spacing; - if (x >= offset_x && x < win_w - 8 && y >= item_y && y < item_y + item_h) { - current_view = VIEW_DISPLAY; - } - } else if (current_view == VIEW_WALLPAPER) { - int offset_x = 8; - int offset_y = 6; - int button_y = offset_y + 65; - int button_x = offset_x; - - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; + if (widget_button_handle_mouse(&btn_main_wallpaper, x, y, is_down, is_click, NULL)) { + if (is_click) { current_view = VIEW_WALLPAPER; focused_field = -1; btn_main_wallpaper.pressed = false; } return; } - - if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_COFFEE, 0, 0, 0); + if (widget_button_handle_mouse(&btn_main_network, x, y, is_down, is_click, NULL)) { + if (is_click) { current_view = VIEW_NETWORK; focused_field = -1; btn_main_network.pressed = false; } return; } - if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_TEAL, 0, 0, 0); + if (widget_button_handle_mouse(&btn_main_desktop, x, y, is_down, is_click, NULL)) { + if (is_click) { current_view = VIEW_DESKTOP; focused_field = -1; btn_main_desktop.pressed = false; } return; } - if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_GREEN, 0, 0, 0); + if (widget_button_handle_mouse(&btn_main_mouse, x, y, is_down, is_click, NULL)) { + if (is_click) { current_view = VIEW_MOUSE; focused_field = -1; btn_main_mouse.pressed = false; } return; } - - button_y += 35; - if (x >= button_x && x < button_x + 91 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_BLUE_BG, 0, 0, 0); + if (widget_button_handle_mouse(&btn_main_fonts, x, y, is_down, is_click, NULL)) { + if (is_click) { + current_view = VIEW_FONTS; focused_field = -1; btn_main_fonts.pressed = false; + if (font_count == 0) load_fonts(); + } return; } - if (x >= button_x + 100 && x < button_x + 191 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_PURPLE, 0, 0, 0); + if (widget_button_handle_mouse(&btn_main_display, x, y, is_down, is_click, NULL)) { + if (is_click) { current_view = VIEW_DISPLAY; focused_field = -1; btn_main_display.pressed = false; } return; } - if (x >= button_x + 200 && x < button_x + 291 && y >= button_y && y < button_y + 25) { - sys_system(1, COLOR_GREY, 0, 0, 0); - return; - } - - button_y += 60; // 40 + 20 - if (x >= button_x && x < button_x + 132 && y >= button_y && y < button_y + 25) { - sys_system(2, (uint64_t)pattern_lumberjack, 0, 0, 0); - return; - } - if (x >= button_x + 145 && x < button_x + 277 && y >= button_y && y < button_y + 25) { - sys_system(2, (uint64_t)pattern_blue_diamond, 0, 0, 0); - return; - } - - button_y += 60; - if (x >= button_x + 25 && x < button_x + 75 && y >= button_y && y < button_y + 18) { - if (focused_field != 0) rgb_r[0] = 0; - focused_field = 0; input_cursor = 0; return; - } - if (x >= button_x + 115 && x < button_x + 165 && y >= button_y && y < button_y + 18) { - if (focused_field != 1) rgb_g[0] = 0; - focused_field = 1; input_cursor = 0; return; - } - if (x >= button_x + 205 && x < button_x + 255 && y >= button_y && y < button_y + 18) { - if (focused_field != 2) rgb_b[0] = 0; - focused_field = 2; input_cursor = 0; return; - } - - if (x >= button_x && x < button_x + 70 && y >= button_y + 25 && y < button_y + 50) { - uint32_t cust = parse_rgb_separate(rgb_r, rgb_g, rgb_b); - sys_system(1, cust, 0, 0, 0); - return; - } - - button_y += 80; - for (int i = 0; i < wallpaper_count; i++) { - int tx = (i % 3) * (WALLPAPER_THUMB_W + 15); - int ty = (i / 3) * (WALLPAPER_THUMB_H + 25); - if (x >= button_x + tx && x < button_x + tx + WALLPAPER_THUMB_W + 8 && - y >= button_y + ty && y < button_y + ty + WALLPAPER_THUMB_H + 20) { - sys_system(31, (uint64_t)wallpapers[i].path, 0, 0, 0); + } + + if (current_view == VIEW_MOUSE) { + if (is_down || is_click) { + int offset_x = 8; + int offset_y = 6; + int section_y = offset_y + 65; + if (x >= offset_x + 60 && x <= offset_x + 260 && y >= section_y && y <= section_y + 20) { + int new_speed = 1 + (x - (offset_x + 60)) * 49 / 200; + if (new_speed < 1) new_speed = 1; + if (new_speed > 50) new_speed = 50; + mouse_speed = new_speed; + save_mouse_config(); return; } } - } else if (current_view == VIEW_NETWORK) { - int offset_x = 8; - int offset_y = 6; - - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - focused_field = -1; - return; + } + + if (current_view == VIEW_DISPLAY) { + if (widget_textbox_handle_mouse(&tb_custom_w, x, y, is_click, NULL)) { + focused_field = 3; disp_sel_res = 5; input_cursor = tb_custom_w.cursor_pos; return; } - - if (x >= offset_x && x < offset_x + 140 && y >= offset_y + 55 && y < offset_y + 80) { - if (sys_system(6, 0, 0, 0, 0) == 0) { - net_status[0] = 'I'; net_status[1] = 'n'; net_status[2] = 'i'; - net_status[3] = 't'; net_status[4] = 'e'; net_status[5] = 'd'; net_status[6] = 0; - } else { - net_status[0] = 'F'; net_status[1] = 'a'; net_status[2] = 'i'; - net_status[3] = 'l'; net_status[4] = 'e'; net_status[5] = 'd'; net_status[6] = 0; - } - return; - } - - int info_y = offset_y + 90 + 20 + 30; // Info + MAC Y positions - - info_y += 20; // IP display - info_y += 30; // DNS display - - // IPSET click bounds - if (x >= offset_x + 60 && x < offset_x + 200 && y >= info_y && y < info_y + 20) { - focused_field = 5; - int len = 0; while (net_ip[len]) len++; input_cursor = len; - return; - } - - // Apply IPSET click block - if (x >= offset_x + 210 && x < offset_x + 260 && y >= info_y && y < info_y + 20) { - net_ipv4_address_t ip; - if (parse_ip(net_ip, &ip) == 0) { - sys_network_set_ip(&ip); - } - return; - } - - info_y += 30; - - // DNSSET click bounds - if (x >= offset_x + 60 && x < offset_x + 200 && y >= info_y && y < info_y + 20) { - focused_field = 6; - int len = 0; while (net_dns[len]) len++; input_cursor = len; - return; - } - - // Apply DNSSET click block - if (x >= offset_x + 210 && x < offset_x + 260 && y >= info_y && y < info_y + 20) { - net_ipv4_address_t ip; - if (parse_ip(net_dns, &ip) == 0) { - sys_set_dns_server(&ip); - } - return; - } - } else if (current_view == VIEW_DESKTOP) { - int offset_x = 8; - int offset_y = 6; - - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - int section_y = offset_y + 65; - if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { - desktop_snap_to_grid = !desktop_snap_to_grid; - if (!desktop_snap_to_grid) desktop_auto_align = 0; - save_desktop_config(); - return; - } - - section_y += 25; - if (x >= offset_x && x < offset_x + 16 && y >= section_y && y < section_y + 16) { - desktop_auto_align = !desktop_auto_align; - if (desktop_auto_align) desktop_snap_to_grid = 1; - save_desktop_config(); - return; - } - - section_y += 25; - if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { - if (desktop_max_rows_per_col > 1) { - desktop_max_rows_per_col--; - save_desktop_config(); - } - } - if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { - if (desktop_max_rows_per_col < 15) desktop_max_rows_per_col++; - save_desktop_config(); - } - - section_y += 25; - if (x >= offset_x + 130 && x < offset_x + 150 && y >= section_y && y < section_y + 20) { - if (desktop_max_cols > 1) { - desktop_max_cols--; - save_desktop_config(); - } - } - if (x >= offset_x + 180 && x < offset_x + 200 && y >= section_y && y < section_y + 20) { - desktop_max_cols++; - save_desktop_config(); - } - } else if (current_view == VIEW_MOUSE) { - int offset_x = 8; - int offset_y = 6; - - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - int section_y = offset_y + 65; - if (x >= offset_x + 60 && x <= offset_x + 260 && y >= section_y && y <= section_y + 20) { - int new_speed = 1 + (x - (offset_x + 60)) * 49 / 200; - if (new_speed < 1) new_speed = 1; - if (new_speed > 50) new_speed = 50; - mouse_speed = new_speed; - save_mouse_config(); - return; - } - } else if (current_view == VIEW_FONTS) { - int offset_x = 8; - int offset_y = 6; - - // Back button - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - // Font items - int item_y = offset_y + 60; - for (int i = 0; i < font_count; i++) { - if (x >= offset_x && x < offset_x + 330 && y >= item_y && y < item_y + 35) { - selected_font = i; - sys_system(40 /*SET_FONT*/, (uint64_t)fonts[i].path, 0, 0, 0); - return; - } - item_y += 40; - } - } else if (current_view == VIEW_DISPLAY) { - int offset_x = 8; - int offset_y = 6; - int right_x = offset_x + 160; - - if (x >= offset_x && x < offset_x + 80 && y >= offset_y + 5 && y < offset_y + 30) { - current_view = VIEW_MAIN; - return; - } - - int btn_y = offset_y + 60; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 0; - if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 0; - - btn_y += 35; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 1; - if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 1; - - btn_y += 35; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 2; - if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 2; - - btn_y += 35; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 3; - if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 3; - - btn_y += 35; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 4; - if (x >= right_x && x < right_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_color = 4; - - btn_y += 35; - if (x >= offset_x && x < offset_x + 140 && y >= btn_y && y < btn_y + 30) disp_sel_res = 5; - - // Custom Inputs - btn_y += 35; - if (x >= offset_x && x < offset_x + 60 && y >= btn_y && y < btn_y + 25) { - focused_field = 3; disp_sel_res = 5; - int len = 0; while (custom_res_w[len]) len++; input_cursor = len; - } - if (x >= offset_x + 80 && x < offset_x + 140 && y >= btn_y && y < btn_y + 25) { - focused_field = 4; disp_sel_res = 5; - int len = 0; while (custom_res_h[len]) len++; input_cursor = len; - } - - btn_y = offset_y + 320; - if (x >= offset_x && x < offset_x + 300 && y >= btn_y && y < btn_y + 35) { - int w = 1024, h = 768; - if (disp_sel_res == 0) { w = 640; h = 480; } - else if (disp_sel_res == 1) { w = 800; h = 600; } - else if (disp_sel_res >= 2 && disp_sel_res <= 4) { - w = dyn_res_w[disp_sel_res - 2]; - h = dyn_res_h[disp_sel_res - 2]; - } else if (disp_sel_res == 5) { - extern int atoi(const char *str); - int cw = atoi(custom_res_w); - int ch = atoi(custom_res_h); - if (cw >= 320 && ch >= 200) { w = cw; h = ch; } - } - - int bpp = 32, mode = 0; - if (disp_sel_color == 1) { bpp = 16; } - if (disp_sel_color == 2) { bpp = 8; mode = 0; } - if (disp_sel_color == 3) { bpp = 8; mode = 1; } - if (disp_sel_color == 4) { bpp = 8; mode = 2; } - - sys_system(47 /*SET_RESOLUTION*/, w, h, bpp, mode); + if (widget_textbox_handle_mouse(&tb_custom_h, x, y, is_click, NULL)) { + focused_field = 4; disp_sel_res = 5; input_cursor = tb_custom_h.cursor_pos; return; } } } @@ -1155,38 +1052,88 @@ static void control_panel_handle_key(char c, bool pressed) { if (!pressed) return; if (focused_field < 0) return; - if (current_view == VIEW_WALLPAPER || current_view == VIEW_DISPLAY || current_view == VIEW_NETWORK) { - char *focused_buffer = NULL; - int max_len = 3; - - if (focused_field == 0 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_r; - else if (focused_field == 1 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_g; - else if (focused_field == 2 && current_view == VIEW_WALLPAPER) focused_buffer = rgb_b; - else if (focused_field == 3 && current_view == VIEW_DISPLAY) { focused_buffer = custom_res_w; max_len = 5; } - else if (focused_field == 4 && current_view == VIEW_DISPLAY) { focused_buffer = custom_res_h; max_len = 5; } - else if (focused_field == 5 && current_view == VIEW_NETWORK) { focused_buffer = net_ip; max_len = 15; } - else if (focused_field == 6 && current_view == VIEW_NETWORK) { focused_buffer = net_dns; max_len = 15; } - else return; - + if (current_view == VIEW_WALLPAPER) { + if (focused_field == 0) widget_textbox_handle_key(&tb_r, c, NULL); + if (focused_field == 1) widget_textbox_handle_key(&tb_g, c, NULL); + if (focused_field == 2) widget_textbox_handle_key(&tb_b, c, NULL); + if (c == '\t') focused_field = (focused_field + 1) % 3; + } else if (current_view == VIEW_DISPLAY) { + char *focused_buffer = (focused_field == 3) ? custom_res_w : custom_res_h; if (c == '\b') { if (input_cursor > 0) { input_cursor--; focused_buffer[input_cursor] = '\0'; } - } else if ((c >= '0' && c <= '9') || c == '.') { - if (input_cursor < max_len) { + } else if (c >= '0' && c <= '9') { + if (input_cursor < 5) { focused_buffer[input_cursor] = c; input_cursor++; focused_buffer[input_cursor] = '\0'; } - } else if (c == '\t') { - if (current_view == VIEW_WALLPAPER) focused_field = (focused_field + 1) % 3; - else if (current_view == VIEW_NETWORK) focused_field = (focused_field == 5) ? 6 : 5; - input_cursor = 0; } + } else if (current_view == VIEW_NETWORK) { + if (focused_field == 5) widget_textbox_handle_key(&tb_ip, c, NULL); + if (focused_field == 6) widget_textbox_handle_key(&tb_dns, c, NULL); + if (c == '\t') focused_field = (focused_field == 5) ? 6 : 5; } } +static void init_settings_widgets(void) { + widget_checkbox_init(&chk_snap, 8, 71, 150, 20, "Snap to Grid", false); + widget_checkbox_init(&chk_align, 8, 96, 150, 20, "Auto Align Icons", false); + + static const char *res_opts[] = {"640x480", "800x600", dyn_res_str[0], dyn_res_str[1], dyn_res_str[2], "Custom"}; + widget_dropdown_init(&drop_res, 8, 66, 140, 30, res_opts, 6); + + static const char *color_opts[] = {"32-bit", "16-bit", "256 Colors", "Grayscale", "Monochrome"}; + widget_dropdown_init(&drop_color, 168, 66, 140, 30, color_opts, 5); + + widget_textbox_init(&tb_r, 33, 226, 50, 18, rgb_r, 4); + widget_textbox_init(&tb_g, 123, 226, 50, 18, rgb_g, 4); + widget_textbox_init(&tb_b, 213, 226, 50, 18, rgb_b, 4); + + widget_textbox_init(&tb_ip, 68, 196, 140, 20, net_ip, 16); + widget_textbox_init(&tb_dns, 68, 226, 140, 20, net_dns, 16); + + widget_button_init(&btn_back, 8, 11, 80, 25, "< Back"); + widget_button_init(&btn_apply, 8, 326, 300, 35, "Apply"); + + // Main Menu Buttons + int item_y = 0; + widget_button_init(&btn_main_wallpaper, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_network, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_desktop, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_mouse, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_fonts, 8, 6 + item_y, 334, 60, ""); item_y += 70; + widget_button_init(&btn_main_display, 8, 6 + item_y, 334, 60, ""); + + // Wallpaper View Buttons + widget_button_init(&btn_wp_colors[0], 8, 71, 91, 25, ""); + widget_button_init(&btn_wp_colors[1], 108, 71, 91, 25, ""); + widget_button_init(&btn_wp_colors[2], 208, 71, 91, 25, ""); + widget_button_init(&btn_wp_colors[3], 8, 106, 91, 25, ""); + widget_button_init(&btn_wp_colors[4], 108, 106, 91, 25, ""); + widget_button_init(&btn_wp_colors[5], 208, 106, 91, 25, ""); + widget_button_init(&btn_wp_patterns[0], 8, 166, 132, 25, ""); + widget_button_init(&btn_wp_patterns[1], 153, 166, 132, 25, ""); + widget_button_init(&btn_wp_apply, 8, 251, 70, 25, ""); + + // Network View Buttons + widget_button_init(&btn_net_init, 8, 61, 140, 25, ""); + widget_button_init(&btn_net_set_ip, 218, 196, 50, 20, ""); + widget_button_init(&btn_net_set_dns, 218, 226, 50, 20, ""); + + // Desktop View Buttons + widget_button_init(&btn_dt_rows_minus, 138, 121, 20, 20, ""); + widget_button_init(&btn_dt_rows_plus, 188, 121, 20, 20, ""); + widget_button_init(&btn_dt_cols_minus, 138, 151, 20, 20, ""); + widget_button_init(&btn_dt_cols_plus, 188, 151, 20, 20, ""); + + // Display View Textboxes + widget_textbox_init(&tb_custom_w, 8, 276, 60, 25, custom_res_w, 5); + widget_textbox_init(&tb_custom_h, 88, 276, 60, 25, custom_res_h, 5); +} + int main(int argc, char **argv) { (void)argc; (void)argv; @@ -1197,14 +1144,22 @@ int main(int argc, char **argv) { fetch_kernel_state(); + init_settings_widgets(); + + // Set initial toggle states + chk_snap.checked = desktop_snap_to_grid; + chk_align.checked = desktop_auto_align; + drop_res.selected_idx = disp_sel_res; + drop_color.selected_idx = disp_sel_color; + gui_event_t ev; while (1) { if (ui_get_event(win, &ev)) { if (ev.type == GUI_EVENT_PAINT) { control_panel_paint(win); ui_mark_dirty(win, 0, 0, 350, 500); - } else if (ev.type == GUI_EVENT_CLICK) { - control_panel_handle_click(ev.arg1, ev.arg2); + } else if (ev.type == GUI_EVENT_CLICK || ev.type == GUI_EVENT_MOUSE_DOWN) { + control_panel_handle_mouse(ev.arg1, ev.arg2, ev.type == GUI_EVENT_MOUSE_DOWN, ev.type == GUI_EVENT_CLICK); control_panel_paint(win); ui_mark_dirty(win, 0, 0, 350, 500); } else if (ev.type == GUI_EVENT_KEY) { diff --git a/src/core/cmd.c b/src/wm/cmd.c similarity index 100% rename from src/core/cmd.c rename to src/wm/cmd.c diff --git a/src/core/cmd.h b/src/wm/cmd.h similarity index 100% rename from src/core/cmd.h rename to src/wm/cmd.h diff --git a/src/wm/explorer.c b/src/wm/explorer.c index c9eae17..1e8c6a2 100644 --- a/src/wm/explorer.c +++ b/src/wm/explorer.c @@ -42,6 +42,28 @@ static int dropdown_menu_item_height = 25; #define FILE_CONTEXT_MENU_HEIGHT 50 #define CONTEXT_MENU_ITEM_HEIGHT 25 +static void wm_draw_rect(void *user_data, int x, int y, int w, int h, uint32_t color) { + (void)user_data; draw_rect(x, y, w, h, color); +} +static void wm_draw_rounded_rect_filled(void *user_data, int x, int y, int w, int h, int r, uint32_t color) { + (void)user_data; draw_rounded_rect_filled(x, y, w, h, r, color); +} +static void wm_draw_string(void *user_data, int x, int y, const char *str, uint32_t color) { + (void)user_data; draw_string(x, y, str, color); +} +static int wm_measure_string_width(void *user_data, const char *str) { + (void)user_data; + return font_manager_get_string_width(graphics_get_current_ttf(), str); +} +static widget_context_t wm_widget_ctx = { + .user_data = NULL, + .draw_rect = wm_draw_rect, + .draw_rounded_rect_filled = wm_draw_rounded_rect_filled, + .draw_string = wm_draw_string, + .measure_string_width = wm_measure_string_width, + .mark_dirty = NULL +}; + static char clipboard_path[FAT32_MAX_PATH] = ""; static int clipboard_action = 0; #define FILE_CONTEXT_ITEMS 2 @@ -950,16 +972,15 @@ static void explorer_paint(Window *win) { draw_string(path_x + 6 + path_label_w + 6, offset_y + 8, state->current_path, COLOR_DARK_TEXT); int dropdown_btn_x = win->x + win->w - 90; - draw_rounded_rect_filled(dropdown_btn_x, offset_y + 3, 35, 22, 5, COLOR_DARK_PANEL); - draw_string(dropdown_btn_x + 10, offset_y + 8, "...", COLOR_DARK_TEXT); + widget_button_init(&state->btn_dropdown, dropdown_btn_x, offset_y + 3, 35, 22, "..."); + widget_button_init(&state->btn_back, win->x + win->w - 40, offset_y + 3, 30, 22, "<"); + widget_button_init(&state->btn_up, win->x + win->w - 160, offset_y + 3, 30, 22, "^"); + widget_button_init(&state->btn_fwd, win->x + win->w - 125, offset_y + 3, 30, 22, "v"); - draw_rounded_rect_filled(win->x + win->w - 40, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL); - draw_string(win->x + win->w - 32, offset_y + 8, "<", COLOR_DARK_TEXT); - - draw_rounded_rect_filled(win->x + win->w - 160, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL); - draw_string(win->x + win->w - 150, offset_y + 8, "^", COLOR_DARK_TEXT); - draw_rounded_rect_filled(win->x + win->w - 125, offset_y + 3, 30, 22, 5, COLOR_DARK_PANEL); - draw_string(win->x + win->w - 115, offset_y + 8, "v", COLOR_DARK_TEXT); + widget_button_draw(&wm_widget_ctx, &state->btn_dropdown); + widget_button_draw(&wm_widget_ctx, &state->btn_back); + widget_button_draw(&wm_widget_ctx, &state->btn_up); + widget_button_draw(&wm_widget_ctx, &state->btn_fwd); int content_start_y = offset_y + 30; @@ -1043,25 +1064,15 @@ static void explorer_paint(Window *win) { draw_string(dlg_x + 10, dlg_y + 10, "Create New File", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); - draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); - { int max_w = 265; - ttf_font_t *ttf_ = graphics_get_current_ttf(); - int total_w = font_manager_get_string_width(ttf_, state->dialog_input); - int scroll_x = 0; - if (total_w > max_w) scroll_x = total_w - max_w; - char sub_[128]; int k_=0; - for(k_=0; k_dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_]; - sub_[k_]=0; - int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x; - if (cx_ < 0) cx_ = 0; - if (cx_ > max_w) cx_ = max_w; - draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); } + widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX); + state->dialog_textbox.focused = true; + state->dialog_textbox.cursor_pos = state->dialog_input_cursor; + widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox); - draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 70, dlg_y + 72, "Create", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Create"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } else if (state->dialog_state == DIALOG_CREATE_FOLDER) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; @@ -1070,25 +1081,15 @@ static void explorer_paint(Window *win) { draw_string(dlg_x + 10, dlg_y + 10, "Create New Folder", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); - draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); - { int max_w = 265; - ttf_font_t *ttf_ = graphics_get_current_ttf(); - int total_w = font_manager_get_string_width(ttf_, state->dialog_input); - int scroll_x = 0; - if (total_w > max_w) scroll_x = total_w - max_w; - char sub_[128]; int k_=0; - for(k_=0; k_dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_]; - sub_[k_]=0; - int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x; - if (cx_ < 0) cx_ = 0; - if (cx_ > max_w) cx_ = max_w; - draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); } + widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX); + state->dialog_textbox.focused = true; + state->dialog_textbox.cursor_pos = state->dialog_input_cursor; + widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox); - draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 70, dlg_y + 72, "Create", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Create"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } else if (state->dialog_state == DIALOG_DELETE_CONFIRM) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; @@ -1107,8 +1108,14 @@ static void explorer_paint(Window *win) { } draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, 0xFF8B2020); draw_string(dlg_x + 68, dlg_y + 72, "Delete", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); + + // Use libwidget only for the cancel button, or do we want to use libwidget for the delete button too? + // Let's use libwidget but the delete button needs red styling so maybe just keep it manual or make it secondary. + // Actually wait, I will use libwidget for both and let the text dictate the action, we can't style individual buttons yet. + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Delete"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; @@ -1120,10 +1127,10 @@ static void explorer_paint(Window *win) { draw_string(dlg_x + 10, dlg_y + 35, "Replace existing file?", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA); - draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 63, dlg_y + 77, "Replace", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE); + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Replace"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; @@ -1150,10 +1157,10 @@ static void explorer_paint(Window *win) { draw_string(dlg_x + 10, dlg_y + 35, "Overwrite existing file?", 0xFFAAAAAA); draw_string(dlg_x + 10, dlg_y + 48, "This cannot be undone.", 0xFFAAAAAA); - draw_rounded_rect_filled(dlg_x + 50, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 57, dlg_y + 77, "Overwrite", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 77, "Cancel", COLOR_WHITE); + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Overwrite"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } else if (state->dialog_state == DIALOG_ERROR) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; @@ -1163,32 +1170,23 @@ static void explorer_paint(Window *win) { draw_string(dlg_x + 10, dlg_y + 10, "Error", 0xFFFF6B6B); draw_string(dlg_x + 10, dlg_y + 40, state->dialog_input, 0xFFAAAAAA); - draw_rounded_rect_filled(dlg_x + 110, dlg_y + 70, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 138, dlg_y + 77, "OK", COLOR_WHITE); + widget_button_init(&state->btn_primary, dlg_x + 110, dlg_y + 70, 80, 25, "OK"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); } else if (state->dialog_state == DIALOG_RENAME) { int dlg_x = win->x + win->w / 2 - 150; int dlg_y = win->y + win->h / 2 - 60; draw_rounded_rect_filled(dlg_x, dlg_y, 300, 110, 8, COLOR_DARK_PANEL); draw_string(dlg_x + 10, dlg_y + 10, "Rename", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 10, dlg_y + 35, 280, 20, 4, COLOR_DARK_BG); - draw_string(dlg_x + 15, dlg_y + 40, state->dialog_input, COLOR_WHITE); - { int max_w = 265; - ttf_font_t *ttf_ = graphics_get_current_ttf(); - int total_w = font_manager_get_string_width(ttf_, state->dialog_input); - int scroll_x = 0; - if (total_w > max_w) scroll_x = total_w - max_w; - char sub_[128]; int k_=0; - for(k_=0; k_dialog_input_cursor && state->dialog_input[k_]; k_++) sub_[k_]=state->dialog_input[k_]; - sub_[k_]=0; - int cx_ = font_manager_get_string_width(ttf_, sub_) - scroll_x; - if (cx_ < 0) cx_ = 0; - if (cx_ > max_w) cx_ = max_w; - draw_rect(dlg_x+15+cx_, dlg_y+39, 2, 12, COLOR_WHITE); } - draw_rounded_rect_filled(dlg_x + 50, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 68, dlg_y + 72, "Rename", COLOR_WHITE); - draw_rounded_rect_filled(dlg_x + 170, dlg_y + 65, 80, 25, 6, COLOR_DARK_BORDER); - draw_string(dlg_x + 185, dlg_y + 72, "Cancel", COLOR_WHITE); + widget_textbox_init(&state->dialog_textbox, dlg_x + 10, dlg_y + 35, 280, 25, state->dialog_input, DIALOG_INPUT_MAX); + state->dialog_textbox.focused = true; + state->dialog_textbox.cursor_pos = state->dialog_input_cursor; + widget_textbox_draw(&wm_widget_ctx, &state->dialog_textbox); + + widget_button_init(&state->btn_primary, dlg_x + 50, dlg_y + 70, 80, 25, "Rename"); + widget_button_init(&state->btn_secondary, dlg_x + 170, dlg_y + 70, 80, 25, "Cancel"); + widget_button_draw(&wm_widget_ctx, &state->btn_primary); + widget_button_draw(&wm_widget_ctx, &state->btn_secondary); } if (state->file_context_menu_visible) { @@ -1220,6 +1218,9 @@ static void explorer_paint(Window *win) { } +#define WIDGET_CLICKED(btn, cx, cy) ((cx) >= (btn)->x && (cx) < (btn)->x + (btn)->w && (cy) >= (btn)->y && (cy) < (btn)->y + (btn)->h) +#define TEXTBOX_CLICKED(tb, cx, cy) ((cx) >= (tb)->x && (cx) < (tb)->x + (tb)->w && (cy) >= (tb)->y && (cy) < (tb)->y + (tb)->h) + static void explorer_handle_click(Window *win, int x, int y) { ExplorerState *state = (ExplorerState*)win->data; if (state->file_context_menu_visible) { @@ -1228,106 +1229,90 @@ static void explorer_handle_click(Window *win, int x, int y) { } if (state->dialog_state == DIALOG_CREATE_FILE || state->dialog_state == DIALOG_CREATE_FOLDER) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && - y >= dlg_y + 65 && y < dlg_y + 90) { - if (state->dialog_state == DIALOG_CREATE_FILE) { - dialog_confirm_create_file(win); - } else { - dialog_confirm_create_folder(win); - } + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; + if (state->dialog_state == DIALOG_CREATE_FILE) dialog_confirm_create_file(win); + else dialog_confirm_create_folder(win); return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && - y >= dlg_y + 65 && y < dlg_y + 90) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } - if (x >= dlg_x + 10 && x < dlg_x + 290 && - y >= dlg_y + 35 && y < dlg_y + 55) { - state->dialog_input_cursor = (x - dlg_x - 15) / 8; + if (TEXTBOX_CLICKED(&state->dialog_textbox, win->x + x, win->y + y)) { + state->dialog_input_cursor = (win->x + x - state->dialog_textbox.x - 5) / 8; if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) { state->dialog_input_cursor = explorer_strlen(state->dialog_input); } + if (state->dialog_input_cursor < 0) state->dialog_input_cursor = 0; return; } return; } else if (state->dialog_state == DIALOG_DELETE_CONFIRM) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && - y >= dlg_y + 65 && y < dlg_y + 90) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; dialog_confirm_delete(win); return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && - y >= dlg_y + 65 && y < dlg_y + 90) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } return; } else if (state->dialog_state == DIALOG_REPLACE_CONFIRM) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; dialog_confirm_replace(win); return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } return; } else if (state->dialog_state == DIALOG_REPLACE_MOVE_CONFIRM) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; dialog_confirm_replace_move(win); return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } return; } else if (state->dialog_state == DIALOG_CREATE_REPLACE_CONFIRM) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; dialog_force_create_file(win); return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } return; } else if (state->dialog_state == DIALOG_ERROR) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 110 && x < dlg_x + 190 && y >= dlg_y + 70 && y < dlg_y + 95) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; dialog_close(win); return; } return; } else if (state->dialog_state == DIALOG_RENAME) { - int dlg_x = win->w / 2 - 150; - int dlg_y = win->h / 2 - 80; - - if (x >= dlg_x + 50 && x < dlg_x + 130 && y >= dlg_y + 65 && y < dlg_y + 90) { + if (WIDGET_CLICKED(&state->btn_primary, win->x + x, win->y + y + 20)) { + state->btn_primary.pressed = false; char new_path[FAT32_MAX_PATH]; explorer_strcpy(new_path, state->current_path); if (new_path[explorer_strlen(new_path)-1] != '/') explorer_strcat(new_path, "/"); @@ -1338,16 +1323,17 @@ static void explorer_handle_click(Window *win, int x, int y) { return; } - if (x >= dlg_x + 170 && x < dlg_x + 250 && y >= dlg_y + 65 && y < dlg_y + 90) { + if (WIDGET_CLICKED(&state->btn_secondary, win->x + x, win->y + y + 20)) { + state->btn_secondary.pressed = false; dialog_close(win); return; } - - if (x >= dlg_x + 10 && x < dlg_x + 290 && y >= dlg_y + 35 && y < dlg_y + 55) { - state->dialog_input_cursor = (x - dlg_x - 15) / 8; + if (TEXTBOX_CLICKED(&state->dialog_textbox, win->x + x, win->y + y + 20)) { + state->dialog_input_cursor = (win->x + x - state->dialog_textbox.x - 5) / 8; if (state->dialog_input_cursor > (int)explorer_strlen(state->dialog_input)) { state->dialog_input_cursor = explorer_strlen(state->dialog_input); } + if (state->dialog_input_cursor < 0) state->dialog_input_cursor = 0; return; } return; @@ -1420,27 +1406,27 @@ static void explorer_handle_click(Window *win, int x, int y) { return; } - if (x >= win->w - 90 && x < win->w - 55 && - y >= button_y && y < button_y + 22) { + if (WIDGET_CLICKED(&state->btn_dropdown, win->x + x, win->y + y + 20)) { + state->btn_dropdown.pressed = false; dropdown_menu_toggle(win); state->drive_menu_visible = false; return; } - if (x >= win->w - 40 && x < win->w - 10 && - y >= button_y && y < button_y + 22) { + if (WIDGET_CLICKED(&state->btn_back, win->x + x, win->y + y + 20)) { + state->btn_back.pressed = false; explorer_navigate_to(win, ".."); return; } - if (x >= win->w - 160 && x < win->w - 130 && - y >= button_y && y < button_y + 22) { + if (WIDGET_CLICKED(&state->btn_up, win->x + x, win->y + y + 20)) { + state->btn_up.pressed = false; if (state->explorer_scroll_row > 0) state->explorer_scroll_row--; return; } - if (x >= win->w - 125 && x < win->w - 95 && - y >= button_y && y < button_y + 22) { + if (WIDGET_CLICKED(&state->btn_fwd, win->x + x, win->y + y + 20)) { + state->btn_fwd.pressed = false; int total_rows = (state->item_count + EXPLORER_COLS - 1) / EXPLORER_COLS; if (total_rows == 0) total_rows = 1; if (state->explorer_scroll_row < total_rows - (EXPLORER_ROWS - 1)) state->explorer_scroll_row++; diff --git a/src/wm/explorer.h b/src/wm/explorer.h index b462e6c..835183f 100644 --- a/src/wm/explorer.h +++ b/src/wm/explorer.h @@ -7,6 +7,7 @@ #include "wm.h" #include "fat32.h" #include +#include "libwidget.h" // External windows references (for opening other apps) extern Window win_explorer; @@ -55,6 +56,16 @@ typedef struct { int file_context_menu_y; int file_context_menu_item; + // GUI widgets + widget_button_t btn_primary; + widget_button_t btn_secondary; + widget_button_t btn_dropdown; + widget_button_t btn_back; + widget_button_t btn_up; + widget_button_t btn_fwd; + + widget_textbox_t dialog_textbox; + } ExplorerState; void explorer_init(void); diff --git a/src/wm/graphics.c b/src/wm/graphics.c index 8a2ed9e..df1adf3 100644 --- a/src/wm/graphics.c +++ b/src/wm/graphics.c @@ -287,28 +287,39 @@ static int isqrt(int n) { void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) { if (radius > w / 2) radius = w / 2; if (radius > h / 2) radius = h / 2; - if (radius < 1) radius = 1; + if (radius < 1) { + // Draw a simple rect outline if no radius + draw_rect(x, y, w, 1, color); + draw_rect(x, y + h - 1, w, 1, color); + draw_rect(x, y + 1, 1, h - 2, color); + draw_rect(x + w - 1, y + 1, 1, h - 2, color); + return; + } - // Draw top and bottom edges + // Draw top and bottom straight edges draw_rect(x + radius, y, w - 2*radius, 1, color); draw_rect(x + radius, y + h - 1, w - 2*radius, 1, color); - // Draw left and right edges + // Draw left and right straight edges draw_rect(x, y + radius, 1, h - 2*radius, color); draw_rect(x + w - 1, y + radius, 1, h - 2*radius, color); - // Draw corner circles using integer approximation - for (int i = 0; i < radius; i++) { - int j = isqrt(radius*radius - i*i); + // Draw four corner arcs + for (int dy = 0; dy < radius; dy++) { + int y_dist = radius - 1 - dy; + int dx = isqrt(radius*radius - y_dist*y_dist); + int next_dx = (dy < radius - 1) ? isqrt(radius*radius - (y_dist - 1)*(y_dist - 1)) : radius; - // Top-left corner - put_pixel(x + radius - i - 1, y + radius - j, color); - // Top-right corner - put_pixel(x + w - radius + i, y + radius - j, color); - // Bottom-left corner - put_pixel(x + radius - i - 1, y + h - radius + j - 1, color); - // Bottom-right corner - put_pixel(x + w - radius + i, y + h - radius + j - 1, color); + for (int i = dx; i < next_dx && i <= radius; i++) { + // Top-left + put_pixel(x + radius - 1 - i, y + dy, color); + // Top-right + put_pixel(x + w - radius + i, y + dy, color); + // Bottom-left + put_pixel(x + radius - 1 - i, y + h - 1 - dy, color); + // Bottom-right + put_pixel(x + w - radius + i, y + h - 1 - dy, color); + } } } @@ -316,25 +327,21 @@ void draw_rounded_rect(int x, int y, int w, int h, int radius, uint32_t color) { void draw_rounded_rect_filled(int x, int y, int w, int h, int radius, uint32_t color) { if (radius > w / 2) radius = w / 2; if (radius > h / 2) radius = h / 2; - if (radius < 1) radius = 1; + if (radius < 1) { + draw_rect(x, y, w, h, color); + return; + } - // Draw main rectangle body (center part without corners) - draw_rect(x + radius, y, w - 2*radius, h, color); - draw_rect(x, y + radius, radius, h - 2*radius, color); - draw_rect(x + w - radius, y + radius, radius, h - 2*radius, color); + // Draw main rectangle body + draw_rect(x, y + radius, w, h - 2*radius, color); + // Draw rounded top and bottom caps for (int dy = 0; dy < radius; dy++) { - int dx_top = isqrt(radius*radius - (radius - dy) * (radius - dy)); + int y_dist = radius - 1 - dy; + int dx = isqrt(radius*radius - y_dist*y_dist); - int dx_bottom = isqrt(radius*radius - dy*dy); - - draw_rect(x + radius - dx_top, y + dy, dx_top, 1, color); - - draw_rect(x + w - radius, y + dy, dx_top, 1, color); - - draw_rect(x + radius - dx_bottom, y + h - radius + dy, dx_bottom, 1, color); - - draw_rect(x + w - radius, y + h - radius + dy, dx_bottom, 1, color); + draw_rect(x + radius - dx, y + dy, w - 2*radius + 2*dx, 1, color); + draw_rect(x + radius - dx, y + h - 1 - dy, w - 2*radius + 2*dx, 1, color); } } diff --git a/src/wm/libwidget.c b/src/wm/libwidget.c new file mode 100644 index 0000000..a5e877b --- /dev/null +++ b/src/wm/libwidget.c @@ -0,0 +1,380 @@ +#include "libwidget.h" +#include + +#define COLOR_GRAY 0xFFC0C0C0 +#define COLOR_LTGRAY 0xFFDFDFDF +#define COLOR_DKGRAY 0xFF808080 +#define COLOR_WHITE 0xFFFFFFFF +#define COLOR_BLACK 0xFF000000 + +#define COLOR_SCROLLBAR_BG 0xFF2A2A2A +#define COLOR_SCROLLBAR_THUMB 0xFF505050 +#define COLOR_SCROLLBAR_THUMB_HOVER 0xFF707070 +#define COLOR_SCROLLBAR_THUMB_DRAG 0xFF909090 + +static size_t string_len(const char *str) { + size_t l = 0; + while(str && str[l]) l++; + return l; +} + +#define MAC_BTN_BORDER 0xFF4A4A4C +#define MAC_BTN_BG_NORMAL 0xFF353537 +#define MAC_BTN_BG_HOVER 0xFF454547 +#define MAC_BTN_BG_PRESSED 0xFF555557 + +// --- Button Implementation --- + +void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text) { + btn->x = x; + btn->y = y; + btn->w = w; + btn->h = h; + btn->text = text; + btn->pressed = false; + btn->hovered = false; + btn->on_click = NULL; +} + +void widget_button_draw(widget_context_t *ctx, widget_button_t *btn) { + uint32_t bg_color = MAC_BTN_BG_NORMAL; + if (btn->pressed) { + bg_color = MAC_BTN_BG_PRESSED; + } else if (btn->hovered) { + bg_color = MAC_BTN_BG_HOVER; + } + + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, btn->x, btn->y, btn->w, btn->h, 6, MAC_BTN_BORDER); + ctx->draw_rounded_rect_filled(ctx->user_data, btn->x + 1, btn->y + 1, btn->w - 2, btn->h - 2, 5, bg_color); + } else if (ctx->draw_rect) { + ctx->draw_rect(ctx->user_data, btn->x, btn->y, btn->w, btn->h, MAC_BTN_BORDER); + ctx->draw_rect(ctx->user_data, btn->x + 1, btn->y + 1, btn->w - 2, btn->h - 2, bg_color); + } + + if (btn->text && ctx->draw_string) { + int len = string_len(btn->text); + int tx = btn->x + (btn->w - (len * 8)) / 2; + int ty = btn->y + (btn->h - 8) / 2; + ctx->draw_string(ctx->user_data, tx, ty, btn->text, COLOR_WHITE); + } +} + +bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data) { + bool in_bounds = (mx >= btn->x && mx < btn->x + btn->w && my >= btn->y && my < btn->y + btn->h); + + btn->hovered = in_bounds; + + if (mouse_clicked && in_bounds) { + btn->pressed = true; + return true; + } + + if (!mouse_down && btn->pressed) { + btn->pressed = false; + if (in_bounds && btn->on_click) { + btn->on_click(user_data); + } + return true; + } + + return in_bounds; +} + +// --- Scrollbar Implementation --- + +void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h) { + sb->x = x; + sb->y = y; + sb->w = w; + sb->h = h; + sb->content_height = h; + sb->scroll_y = 0; + sb->is_dragging = false; + sb->on_scroll = NULL; +} + +void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y) { + sb->content_height = content_height; + sb->scroll_y = scroll_y; +} + +void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb) { + // Only draw thumb if content is larger than view + if (sb->content_height > sb->h) { + int thumb_h = (sb->h * sb->h) / sb->content_height; + if (thumb_h < 20) thumb_h = 20; + + int max_scroll = sb->content_height - sb->h; + if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll; + if (sb->scroll_y < 0) sb->scroll_y = 0; + + int thumb_y = sb->y + (sb->scroll_y * (sb->h - thumb_h)) / max_scroll; + + uint32_t color = 0xFF888888; // Subtle gray thumb for mac style + if (sb->is_dragging) color = 0xFF666666; + + if (ctx->draw_rounded_rect_filled) { + // Pill shaped thumb with margin + int margin = 2; + int radius = (sb->w - margin*2) / 2; + ctx->draw_rounded_rect_filled(ctx->user_data, sb->x + margin, thumb_y + margin, sb->w - margin*2, thumb_h - margin*2, radius, color); + } else if (ctx->draw_rect) { + ctx->draw_rect(ctx->user_data, sb->x, thumb_y, sb->w, thumb_h, color); + } + } +} + +bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data) { + if (sb->content_height <= sb->h) return false; + + int thumb_h = (sb->h * sb->h) / sb->content_height; + if (thumb_h < 20) thumb_h = 20; + + int max_scroll = sb->content_height - sb->h; + if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll; + if (sb->scroll_y < 0) sb->scroll_y = 0; + + int thumb_y = sb->y + (sb->scroll_y * (sb->h - thumb_h)) / max_scroll; + + bool in_thumb = (mx >= sb->x && mx < sb->x + sb->w && my >= thumb_y && my < thumb_y + thumb_h); + bool in_track = (mx >= sb->x && mx < sb->x + sb->w && my >= sb->y && my < sb->y + sb->h); + + if (mouse_down && !sb->is_dragging) { + if (in_thumb) { + sb->is_dragging = true; + sb->drag_start_my = my; + sb->drag_start_scroll_y = sb->scroll_y; + return true; + } else if (in_track) { + // Page scroll + if (my < thumb_y) { + sb->scroll_y -= sb->h; + } else { + sb->scroll_y += sb->h; + } + if (sb->scroll_y < 0) sb->scroll_y = 0; + if (sb->scroll_y > max_scroll) sb->scroll_y = max_scroll; + if (sb->on_scroll) sb->on_scroll(user_data, sb->scroll_y); + return true; + } + } else if (!mouse_down) { + sb->is_dragging = false; + } + + if (sb->is_dragging && mouse_down) { + int dy = my - sb->drag_start_my; + int track_h = sb->h - thumb_h; + if (track_h > 0) { + float ratio = (float)max_scroll / (float)track_h; + int new_scroll = sb->drag_start_scroll_y + (int)(dy * ratio); + + if (new_scroll < 0) new_scroll = 0; + if (new_scroll > max_scroll) new_scroll = max_scroll; + + if (new_scroll != sb->scroll_y) { + sb->scroll_y = new_scroll; + if (sb->on_scroll) sb->on_scroll(user_data, sb->scroll_y); + } + } + return true; + } + + return in_track || sb->is_dragging; +} + +// --- TextBox Implementation --- +void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len) { + tb->x = x; tb->y = y; tb->w = w; tb->h = h; + tb->text = buffer; + tb->max_len = max_len; + tb->cursor_pos = string_len(buffer); + tb->focused = false; + tb->on_change = NULL; +} + +void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb) { + // Background and border + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, tb->x, tb->y, tb->w, tb->h, 4, MAC_BTN_BORDER); + ctx->draw_rounded_rect_filled(ctx->user_data, tb->x + 1, tb->y + 1, tb->w - 2, tb->h - 2, 3, COLOR_BLACK); // dark background + } else if (ctx->draw_rect) { + ctx->draw_rect(ctx->user_data, tb->x, tb->y, tb->w, tb->h, MAC_BTN_BORDER); + ctx->draw_rect(ctx->user_data, tb->x + 1, tb->y + 1, tb->w - 2, tb->h - 2, COLOR_BLACK); + } + + if (ctx->draw_string && tb->text) { + int max_w = tb->w - 15; + int scroll_x = 0; + int text_w = 0; + + if (ctx->measure_string_width) { + text_w = ctx->measure_string_width(ctx->user_data, tb->text); + } else { + text_w = string_len(tb->text) * 8; + } + + if (text_w > max_w) scroll_x = text_w - max_w; + + // Very basic simple drawing, without proper clipping since context lacks it + ctx->draw_string(ctx->user_data, tb->x + 5, tb->y + (tb->h - 8) / 2, tb->text, COLOR_WHITE); + + if (tb->focused && ctx->draw_rect) { + int cx = 0; + if (ctx->measure_string_width) { + // measure up to cursor + char tmp[256]; + int k = 0; + for (k = 0; k < tb->cursor_pos && tb->text[k]; k++) { + tmp[k] = tb->text[k]; + } + tmp[k] = 0; + cx = ctx->measure_string_width(ctx->user_data, tmp); + } else { + cx = tb->cursor_pos * 8; + } + + if (cx > max_w) cx = max_w; // clamped to visible end + + ctx->draw_rect(ctx->user_data, tb->x + 5 + cx, tb->y + (tb->h - 12) / 2, 2, 12, COLOR_WHITE); + } + } +} + +bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data) { + bool in_bounds = (mx >= tb->x && mx < tb->x + tb->w && my >= tb->y && my < tb->y + tb->h); + if (mouse_clicked) { + tb->focused = in_bounds; + } + return in_bounds; +} + +bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data) { + if (!tb->focused || !tb->text) return false; + + int len = string_len(tb->text); + if (c == '\b') { // backspace + if (len > 0) { + tb->text[len - 1] = '\0'; + tb->cursor_pos = len - 1; + if (tb->on_change) tb->on_change(user_data); + } + } else if (c >= 32 && c < 127) { + if (len < tb->max_len - 1) { + tb->text[len] = c; + tb->text[len + 1] = '\0'; + tb->cursor_pos = len + 1; + if (tb->on_change) tb->on_change(user_data); + } + } + return true; +} + +// --- Dropdown Implementation --- +void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count) { + dd->x = x; dd->y = y; dd->w = w; dd->h = h; + dd->items = items; + dd->item_count = count; + dd->selected_idx = 0; + dd->is_open = false; + dd->on_select = NULL; +} + +void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd) { + if (ctx->draw_rounded_rect_filled) { + ctx->draw_rounded_rect_filled(ctx->user_data, dd->x, dd->y, dd->w, dd->h, 4, MAC_BTN_BORDER); + ctx->draw_rounded_rect_filled(ctx->user_data, dd->x + 1, dd->y + 1, dd->w - 2, dd->h - 2, 3, MAC_BTN_BG_NORMAL); + } else if (ctx->draw_rect) { + ctx->draw_rect(ctx->user_data, dd->x, dd->y, dd->w, dd->h, MAC_BTN_BORDER); + ctx->draw_rect(ctx->user_data, dd->x + 1, dd->y + 1, dd->w - 2, dd->h - 2, MAC_BTN_BG_NORMAL); + } + + if (ctx->draw_string && dd->items && dd->item_count > 0 && dd->selected_idx >= 0 && dd->selected_idx < dd->item_count) { + ctx->draw_string(ctx->user_data, dd->x + 5, dd->y + (dd->h - 8) / 2, dd->items[dd->selected_idx], COLOR_WHITE); + ctx->draw_string(ctx->user_data, dd->x + dd->w - 12, dd->y + (dd->h - 8) / 2, "v", COLOR_WHITE); + } + + if (dd->is_open && ctx->draw_rect && dd->items) { + int menu_h = dd->item_count * dd->h; + ctx->draw_rect(ctx->user_data, dd->x, dd->y + dd->h, dd->w, menu_h, MAC_BTN_BORDER); + ctx->draw_rect(ctx->user_data, dd->x + 1, dd->y + dd->h + 1, dd->w - 2, menu_h - 2, MAC_BTN_BG_NORMAL); + for (int i = 0; i < dd->item_count; i++) { + if (ctx->draw_string) { + ctx->draw_string(ctx->user_data, dd->x + 5, dd->y + dd->h + i * dd->h + (dd->h - 8)/2, dd->items[i], COLOR_WHITE); + } + } + } +} + +bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data) { + if (!mouse_clicked) return false; + + if (dd->is_open) { + int menu_h = dd->item_count * dd->h; + if (mx >= dd->x && mx < dd->x + dd->w && my >= dd->y + dd->h && my < dd->y + dd->h + menu_h) { + int clicked_idx = (my - (dd->y + dd->h)) / dd->h; + if (clicked_idx >= 0 && clicked_idx < dd->item_count) { + dd->selected_idx = clicked_idx; + dd->is_open = false; + if (dd->on_select) dd->on_select(user_data, clicked_idx); + return true; + } + } + dd->is_open = false; + } + + if (mx >= dd->x && mx < dd->x + dd->w && my >= dd->y && my < dd->y + dd->h) { + dd->is_open = !dd->is_open; + return true; + } + + return false; +} + +// --- Checkbox / Radio Implementation --- +void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio) { + cb->x = x; cb->y = y; cb->w = w; cb->h = h; + cb->text = text; + cb->checked = false; + cb->is_radio = is_radio; + cb->on_toggle = NULL; +} + +void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb) { + int box_size = 14; + int box_y = cb->y + (cb->h - box_size) / 2; + + if (ctx->draw_rounded_rect_filled) { + int radius = cb->is_radio ? box_size / 2 : 3; + ctx->draw_rounded_rect_filled(ctx->user_data, cb->x, box_y, box_size, box_size, radius, MAC_BTN_BORDER); + ctx->draw_rounded_rect_filled(ctx->user_data, cb->x + 1, box_y + 1, box_size - 2, box_size - 2, radius - 1, MAC_BTN_BG_NORMAL); + + if (cb->checked) { + int inner = box_size - 6; + int inner_rad = cb->is_radio ? inner / 2 : 2; + ctx->draw_rounded_rect_filled(ctx->user_data, cb->x + 3, box_y + 3, inner, inner, inner_rad, COLOR_WHITE); + } + } else if (ctx->draw_rect) { + ctx->draw_rect(ctx->user_data, cb->x, box_y, box_size, box_size, MAC_BTN_BORDER); + ctx->draw_rect(ctx->user_data, cb->x + 1, box_y + 1, box_size - 2, box_size - 2, MAC_BTN_BG_NORMAL); + if (cb->checked) { + int inner = box_size - 6; + ctx->draw_rect(ctx->user_data, cb->x + 3, box_y + 3, inner, inner, COLOR_WHITE); + } + } + + if (ctx->draw_string && cb->text) { + ctx->draw_string(ctx->user_data, cb->x + box_size + 8, cb->y + (cb->h - 8) / 2, cb->text, COLOR_WHITE); + } +} + +bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data) { + if (!mouse_clicked) return false; + + if (mx >= cb->x && mx < cb->x + cb->w && my >= cb->y && my < cb->y + cb->h) { + cb->checked = !cb->checked; + if (cb->on_toggle) cb->on_toggle(user_data, cb->checked); + return true; + } + return false; +} diff --git a/src/wm/libwidget.h b/src/wm/libwidget.h new file mode 100644 index 0000000..14702eb --- /dev/null +++ b/src/wm/libwidget.h @@ -0,0 +1,92 @@ +#ifndef LIBWIDGET_H +#define LIBWIDGET_H + +#include +#include + +// Widget Context for abstract drawing backend +typedef struct { + void *user_data; + void (*draw_rect)(void *user_data, int x, int y, int w, int h, uint32_t color); + void (*draw_rounded_rect_filled)(void *user_data, int x, int y, int w, int h, int r, uint32_t color); + void (*draw_string)(void *user_data, int x, int y, const char *str, uint32_t color); + int (*measure_string_width)(void *user_data, const char *str); + void (*mark_dirty)(void *user_data, int x, int y, int w, int h); +} widget_context_t; + +// --- Button --- +typedef struct { + int x, y, w, h; + const char *text; + bool pressed; + bool hovered; + void (*on_click)(void *user_data); +} widget_button_t; + +void widget_button_init(widget_button_t *btn, int x, int y, int w, int h, const char *text); +void widget_button_draw(widget_context_t *ctx, widget_button_t *btn); +// Returns true if event was consumed +bool widget_button_handle_mouse(widget_button_t *btn, int mx, int my, bool mouse_down, bool mouse_clicked, void *user_data); + + +// --- Scrollbar --- +typedef struct { + int x, y, w, h; + int content_height; + int scroll_y; + bool is_dragging; + int drag_start_my; + int drag_start_scroll_y; + void (*on_scroll)(void *user_data, int new_scroll_y); +} widget_scrollbar_t; + +void widget_scrollbar_init(widget_scrollbar_t *sb, int x, int y, int w, int h); +void widget_scrollbar_update(widget_scrollbar_t *sb, int content_height, int scroll_y); +void widget_scrollbar_draw(widget_context_t *ctx, widget_scrollbar_t *sb); +// Returns true if event was consumed +bool widget_scrollbar_handle_mouse(widget_scrollbar_t *sb, int mx, int my, bool mouse_down, void *user_data); + + +// --- TextBox --- +typedef struct { + int x, y, w, h; + char *text; + int max_len; + int cursor_pos; + bool focused; + void (*on_change)(void *user_data); +} widget_textbox_t; + +void widget_textbox_init(widget_textbox_t *tb, int x, int y, int w, int h, char *buffer, int max_len); +void widget_textbox_draw(widget_context_t *ctx, widget_textbox_t *tb); +bool widget_textbox_handle_mouse(widget_textbox_t *tb, int mx, int my, bool mouse_clicked, void *user_data); +bool widget_textbox_handle_key(widget_textbox_t *tb, char c, void *user_data); + +// --- Dropdown --- +typedef struct { + int x, y, w, h; + const char **items; + int item_count; + int selected_idx; + bool is_open; + void (*on_select)(void *user_data, int new_idx); +} widget_dropdown_t; + +void widget_dropdown_init(widget_dropdown_t *dd, int x, int y, int w, int h, const char **items, int count); +void widget_dropdown_draw(widget_context_t *ctx, widget_dropdown_t *dd); +bool widget_dropdown_handle_mouse(widget_dropdown_t *dd, int mx, int my, bool mouse_clicked, void *user_data); + +// --- Checkbox / Radio --- +typedef struct { + int x, y, w, h; + const char *text; + bool checked; + bool is_radio; + void (*on_toggle)(void *user_data, bool new_state); +} widget_checkbox_t; + +void widget_checkbox_init(widget_checkbox_t *cb, int x, int y, int w, int h, const char *text, bool is_radio); +void widget_checkbox_draw(widget_context_t *ctx, widget_checkbox_t *cb); +bool widget_checkbox_handle_mouse(widget_checkbox_t *cb, int mx, int my, bool mouse_clicked, void *user_data); + +#endif