From 93b29de643f7cb788a152f88e36f388746589579 Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 5 Jul 2022 18:29:54 -0500 Subject: [PATCH] Scrollbar widgets and raw mouse handling for widgets. --- roo-e/roo-e.pro | 7 + roo-e/src/gui/gui-all.h | 43 ++++++ roo-e/src/gui/gui.c | 37 +++-- roo-e/src/gui/gui.h | 27 ++-- roo-e/src/gui/widgets/button.c | 12 +- roo-e/src/gui/widgets/checkbox.c | 16 +-- roo-e/src/gui/widgets/checkbox.h | 2 +- roo-e/src/gui/widgets/hscroll.c | 232 +++++++++++++++++++++++++++++++ roo-e/src/gui/widgets/hscroll.h | 54 +++++++ roo-e/src/gui/widgets/label.c | 10 +- roo-e/src/gui/widgets/picture.c | 6 +- roo-e/src/gui/widgets/radio.c | 210 ++++++++++++++++++++++++++++ roo-e/src/gui/widgets/radio.h | 53 +++++++ roo-e/src/gui/widgets/vscroll.c | 224 +++++++++++++++++++++++++++++ roo-e/src/gui/widgets/vscroll.h | 54 +++++++ roo-e/src/gui/wmwindow.c | 115 ++++++++------- roo-e/src/gui/wmwindow.h | 13 +- roo-e/src/main.c | 53 +++++-- 18 files changed, 1064 insertions(+), 104 deletions(-) create mode 100644 roo-e/src/gui/gui-all.h create mode 100644 roo-e/src/gui/widgets/hscroll.c create mode 100644 roo-e/src/gui/widgets/hscroll.h create mode 100644 roo-e/src/gui/widgets/radio.c create mode 100644 roo-e/src/gui/widgets/radio.h create mode 100644 roo-e/src/gui/widgets/vscroll.c create mode 100644 roo-e/src/gui/widgets/vscroll.h diff --git a/roo-e/roo-e.pro b/roo-e/roo-e.pro index b097fa9..45d2adf 100644 --- a/roo-e/roo-e.pro +++ b/roo-e/roo-e.pro @@ -49,6 +49,7 @@ HEADERS += \ $$SHARED/log.h \ $$SHARED/util.h \ src/gui/font.h \ + src/gui/gui-all.h \ src/gui/gui.h \ src/gui/image.h \ src/gui/surface.h \ @@ -56,6 +57,9 @@ HEADERS += \ src/gui/widgets/checkbox.h \ src/gui/widgets/label.h \ src/gui/widgets/picture.h \ + src/gui/widgets/radio.h \ + src/gui/widgets/hscroll.h \ + src/gui/widgets/vscroll.h \ src/gui/wmwindow.h \ src/os.h \ src/platform/platform.h \ @@ -75,6 +79,9 @@ SOURCES += \ src/gui/widgets/checkbox.c \ src/gui/widgets/label.c \ src/gui/widgets/picture.c \ + src/gui/widgets/radio.c \ + src/gui/widgets/hscroll.c \ + src/gui/widgets/vscroll.c \ src/gui/wmwindow.c \ src/main.c \ src/platform/djgpp.c \ diff --git a/roo-e/src/gui/gui-all.h b/roo-e/src/gui/gui-all.h new file mode 100644 index 0000000..b67eef2 --- /dev/null +++ b/roo-e/src/gui/gui-all.h @@ -0,0 +1,43 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef GUIALL_H +#define GUIALL_H + + +// All headers for standard Roo/E features. + +#include "gui.h" +#include "wmwindow.h" +#include "widgets/label.h" +#include "widgets/picture.h" +#include "widgets/button.h" +#include "widgets/checkbox.h" +#include "widgets/radio.h" +#include "widgets/vscroll.h" +#include "widgets/hscroll.h" + + +#endif // GUIALL_H diff --git a/roo-e/src/gui/gui.c b/roo-e/src/gui/gui.c index 79c7492..afb6453 100644 --- a/roo-e/src/gui/gui.c +++ b/roo-e/src/gui/gui.c @@ -23,16 +23,12 @@ */ -#include "gui.h" #include "font.h" #include "image.h" #include "array.h" -#include "wmwindow.h" -#include "widgets/button.h" -#include "widgets/checkbox.h" -#include "widgets/label.h" -#include "widgets/picture.h" +#include "../gui/gui.h" +#include "../gui/gui-all.h" typedef struct WidgetCatalogS { @@ -200,6 +196,9 @@ uint8_t guiStartup(int16_t width, int16_t height, int16_t depth) { guiRegister(buttonRegister); guiRegister(checkboxRegister); guiRegister(pictureRegister); + guiRegister(radioRegister); + guiRegister(vscrollRegister); + guiRegister(hscrollRegister); return SUCCESS; } @@ -210,7 +209,7 @@ void guiStop(void) { } -void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h) { +void widgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h) { widget->magic = magic; widget->r.w = w; @@ -222,18 +221,18 @@ void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h) { } -void guiWidgetDestroy(WidgetT *widget) { +void widgetDestroy(WidgetT *widget) { // Add to list of widgets to delete. arrput(_deleteList, widget); } -uint8_t guiWidgetDirtyGet(WidgetT *widget) { +uint8_t widgetDirtyGet(WidgetT *widget) { return (widget->flags & WIDGET_DIRTY) != 0; } -void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty) { +void widgetDirtySet(WidgetT *widget, uint8_t dirty) { if (dirty) { widget->flags |= WIDGET_DIRTY; } else { @@ -242,10 +241,24 @@ void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty) { } -void guiWidgetPaintManually(WidgetT *widget, int16_t x, int16_t y) { +void widgetInputSetRaw(WidgetT *widget, uint8_t raw) { + if (raw) { + widget->flags |= WIDGET_RAW_INPUT; + } else { + widget->flags &= ~WIDGET_RAW_INPUT; + } +} + + +uint8_t widgetIsInWindow(WidgetT *widget, struct WindowS *window) { + return widget->parent == window;; +} + + +void widgetPaintManually(WidgetT *widget, int16_t x, int16_t y) { RectT r = widget->r; - guiWidgetDirtySet(widget, 1); + widgetDirtySet(widget, 1); widget->r.x = x; widget->r.y = y; widget->reg->paint(widget); diff --git a/roo-e/src/gui/gui.h b/roo-e/src/gui/gui.h index 5033ed3..f83fa80 100644 --- a/roo-e/src/gui/gui.h +++ b/roo-e/src/gui/gui.h @@ -35,10 +35,12 @@ #define WIDGET_DIRTY 1 #define WIDGET_HIDDEN 2 #define WIDGET_DISABLED 4 +#define WIDGET_RAW_INPUT 8 -#define CLICK_LEFT_CANCEL 1 -#define CLICK_LEFT_DOWN 2 -#define CLICK_LEFT_UP 3 +#define CLICK_RAW_INPUT 1 +#define CLICK_LEFT_CANCEL 2 +#define CLICK_LEFT_DOWN 3 +#define CLICK_LEFT_UP 4 struct WidgetS; @@ -80,8 +82,14 @@ typedef struct WidgetS { RegisterT *reg; // Registration information. void *data; // Pointer to arbitrary data for user. uint8_t flags; // Widget flags (see defines above). + struct WindowS *parent; // Who owns this widget? } WidgetT; +typedef struct ClickRawInputS { + void *data; + EventT *event; +} ClickRawInputT; + typedef RegisterT *(*WidgetRegisterT)(uint8_t); @@ -128,11 +136,14 @@ void guiRun(void); void guiShutdown(void); uint8_t guiStartup(int16_t width, int16_t height, int16_t depth); void guiStop(void); -void guiWidgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h); -void guiWidgetDestroy(WidgetT *widget); -uint8_t guiWidgetDirtyGet(WidgetT *widget); -void guiWidgetDirtySet(WidgetT *widget, uint8_t dirty); -void guiWidgetPaintManually(WidgetT *widget, int16_t x, int16_t y); + +void widgetBaseSet(WidgetT *widget, uint8_t magic, uint16_t w, uint16_t h); +void widgetDestroy(WidgetT *widget); +uint8_t widgetDirtyGet(WidgetT *widget); +void widgetDirtySet(WidgetT *widget, uint8_t dirty); +void widgetInputSetRaw(WidgetT *widget, uint8_t raw); +uint8_t widgetIsInWindow(WidgetT *widget, struct WindowS *window); +void widgetPaintManually(WidgetT *widget, int16_t x, int16_t y); #endif // GUI_H diff --git a/roo-e/src/gui/widgets/button.c b/roo-e/src/gui/widgets/button.c index f376866..cdf75d3 100644 --- a/roo-e/src/gui/widgets/button.c +++ b/roo-e/src/gui/widgets/button.c @@ -55,7 +55,7 @@ static void buttonClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, break; } - guiWidgetDirtySet(widget, 1); + widgetDirtySet(widget, 1); } @@ -85,7 +85,7 @@ ButtonT *buttonCreate(char *label, ClickHandlerT handler, void *data, ...) { width = fontWidthCharactersGet(label) * fontWidthGet(f) + (b->offset.x * 2); height = fontHeightGet(f) + (b->offset.y * 2); - guiWidgetBaseSet(W(b), __MAGIC_BUTTON, width, height); + widgetBaseSet(W(b), __MAGIC_BUTTON, width, height); buttonClickSet(b, handler, data); @@ -96,7 +96,7 @@ ButtonT *buttonCreate(char *label, ClickHandlerT handler, void *data, ...) { static void buttonDestroy(struct WidgetS *widget, ...) { ButtonT *b = (ButtonT *)widget; - guiWidgetDestroy(W(b->label)); + widgetDestroy(W(b->label)); DEL(b); } @@ -106,14 +106,14 @@ static void buttonPaint(struct WidgetS *widget, ...) { ColorT h = b->isPressed ? GUI_BLACK : GUI_WHITE; ColorT s = b->isPressed ? GUI_WHITE : GUI_BLACK; - if (guiWidgetDirtyGet(widget)) { - guiWidgetDirtySet(widget, 0); + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); // Paint button. surfaceBoxHighlight(b->base.r.x, b->base.r.y, b->base.r.x + b->base.r.x2 - 1, b->base.r.y + b->base.r.y2 - 1, h, s); surfaceBoxHighlight(b->base.r.x + 1, b->base.r.y + 1, b->base.r.x + b->base.r.x2 - 2, b->base.r.y + b->base.r.y2 - 2, h, s); surfaceBoxFilled(b->base.r.x + 2, b->base.r.y + 2, b->base.r.x + b->base.r.x2 - 3, b->base.r.y + b->base.r.y2 - 3, GUI_LIGHTGRAY); // Paint label. - guiWidgetPaintManually(W(b->label), b->base.r.x + b->offset.x + b->isPressed, b->base.r.y + b->offset.y + b->isPressed); + widgetPaintManually(W(b->label), b->base.r.x + b->offset.x + b->isPressed, b->base.r.y + b->offset.y + b->isPressed); } } diff --git a/roo-e/src/gui/widgets/checkbox.c b/roo-e/src/gui/widgets/checkbox.c index d557bdd..7892109 100644 --- a/roo-e/src/gui/widgets/checkbox.c +++ b/roo-e/src/gui/widgets/checkbox.c @@ -47,7 +47,7 @@ static void checkboxClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event if (c->handler) c->handler(widget, x, y, event, data); // Repaint. - guiWidgetDirtySet(widget, 1); + widgetDirtySet(widget, 1); } } @@ -78,7 +78,7 @@ CheckboxT *checkboxCreate(char *label, uint8_t value, ...) { height = fontHeightGet(f); width = fontWidthCharactersGet(label) * fontWidthGet(f) + (height * 3); - guiWidgetBaseSet(W(c), __MAGIC_CHECKBOX, width, height); + widgetBaseSet(W(c), __MAGIC_CHECKBOX, width, height); return c; } @@ -87,7 +87,7 @@ CheckboxT *checkboxCreate(char *label, uint8_t value, ...) { static void checkboxDestroy(struct WidgetS *widget, ...) { CheckboxT *c = (CheckboxT *)widget; - guiWidgetDestroy(W(c->label)); + widgetDestroy(W(c->label)); DEL(c); } @@ -97,15 +97,15 @@ static void checkboxPaint(struct WidgetS *widget, ...) { int16_t w; int16_t h; - if (guiWidgetDirtyGet(widget)) { - guiWidgetDirtySet(widget, 0); + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); h = fontHeightGet(c->label->font) - 2; w = h * 1.5; // Paint checkbox. - surfaceBoxHighlight(c->base.r.x, c->base.r.y + 1, c->base.r.x + h, c->base.r.y + h, GUI_BLACK, GUI_WHITE); + surfaceBoxHighlight(c->base.r.x, c->base.r.y + 1, c->base.r.x + h, c->base.r.y + h, c->value ? GUI_BLACK : GUI_WHITE, c->value ? GUI_WHITE : GUI_BLACK); surfaceBoxFilled(c->base.r.x + 1, c->base.r.y + 2, c->base.r.x + h - 1, c->base.r.y + h - 1, c->value ? GUI_DARKGRAY : GUI_WHITE); // Paint label. - guiWidgetPaintManually(W(c->label), c->base.r.x + w, c->base.r.y); + widgetPaintManually(W(c->label), c->base.r.x + w, c->base.r.y); } } @@ -134,5 +134,5 @@ uint8_t checkboxValueGet(CheckboxT *checkbox) { void checkboxValueSet(CheckboxT *checkbox, uint8_t value) { checkbox->value = (value != 0); // Repaint. - guiWidgetDirtySet(W(checkbox), 1); + widgetDirtySet(W(checkbox), 1); } diff --git a/roo-e/src/gui/widgets/checkbox.h b/roo-e/src/gui/widgets/checkbox.h index dc3ffaa..ba3e277 100644 --- a/roo-e/src/gui/widgets/checkbox.h +++ b/roo-e/src/gui/widgets/checkbox.h @@ -35,7 +35,7 @@ typedef struct CheckboxS { WidgetT base; // Required by all widgets. LabelT *label; // Label to display text. ClickHandlerT handler; // Actual event handler. - uint8_t value; // Is the button being pressed? + uint8_t value; // Is theis checkbox selected? } CheckboxT; diff --git a/roo-e/src/gui/widgets/hscroll.c b/roo-e/src/gui/widgets/hscroll.c new file mode 100644 index 0000000..b8eef3f --- /dev/null +++ b/roo-e/src/gui/widgets/hscroll.c @@ -0,0 +1,232 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#include "../wmwindow.h" +#include "../font.h" + +#include "hscroll.h" + + +uint8_t __MAGIC_HSCROLL = 0; + + +static void hscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data); +static void hscrollDestroy(struct WidgetS *widget, ...); +static void hscrollPaint(struct WidgetS *widget, ...); + + +static void hscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) { + HscrollT *h = (HscrollT *)widget; + + // Clicking in left arrow? + if (x <= GADGET_SIZE) { + // Move content left. + h->value -= SCROLL_SPEED_SLOW; + // Clip. + if (h->value < h->min) h->value = h->min; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (h->handler) h->handler(widget, x, y, event, data); + return; + } + + // Clicking in right arrow? + if (x >= h->base.r.w - GADGET_SIZE) { + // Move content right. + h->value += SCROLL_SPEED_SLOW; + // Clip. + if (h->value > h->max - h->base.r.w) h->value = h->max - h->base.r.w; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (h->handler) h->handler(widget, x, y, event, data); + return; + } + + // Clicking left of thumb? Also fakes dragging. + if (x < h->thumb.x) { + // Move content left. + h->value -= SCROLL_SPEED_FAST; + // Clip. + if (h->value < h->min) h->value = h->min; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (h->handler) h->handler(widget, x, y, event, data); + return; + } + + // Clicking right of thumb? Also fakes dragging. + if (x > h->thumb.x2) { + // Move content right. + h->value += SCROLL_SPEED_FAST; + // Clip. + if (h->value > h->max - h->base.r.w) h->value = h->max - h->base.r.w; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (h->handler) h->handler(widget, x, y, event, data); + return; + } +} + + +void hscrollClickSet(HscrollT *hscroll, ClickHandlerT handler, void *data) { + hscroll->handler = handler; + hscroll->base.data = data; +} + + +HscrollT *hscrollCreate(int16_t w, ClickHandlerT handler, void *data, ...) { + HscrollT *h = NULL; + + NEW(HscrollT, h); + memset(h, 0, sizeof(HscrollT)); + + h->min = 1; + h->max = 100; + h->value = 1; + + if (w < GADGET_SIZE * 3) w = GADGET_SIZE * 3; + + widgetBaseSet(W(h), __MAGIC_HSCROLL, w, GADGET_SIZE); + + h->handler = handler; + h->base.data = data; + + widgetInputSetRaw(W(h), 1); + + return h; +} + + +static void hscrollDestroy(struct WidgetS *widget, ...) { + HscrollT *h = (HscrollT *)widget; + + DEL(h); +} + + +static void hscrollPaint(struct WidgetS *widget, ...) { + HscrollT *h = (HscrollT *)widget; + ColorT scrollBackgroundColor; + ColorT widgetColor; + RectT r; + int16_t i; + double d; + + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); + // Find some colors. + if (widgetIsInWindow(widget, wmWindowOnTopGet())) { + scrollBackgroundColor = GUI_DARKGRAY; + widgetColor = GUI_LIGHTGRAY; + } else { + scrollBackgroundColor = GUI_LIGHTGRAY; + widgetColor = GUI_DARKGRAY; + } + // Find coordinates. + r = h->base.r; + r.x2 = r.x + h->base.r.w - 1; + r.y2 = r.y + h->base.r.h - 1; + // Paint scrollbar. + surfaceBoxFilled(r.x + 1, r.y + 1, r.x2 - 1, r.y2 - 1, scrollBackgroundColor); + surfaceBoxHighlight(r.x, r.y, r.x2, r.y2, GUI_WHITE, GUI_BLACK); + surfaceLineV(r.x + (GADGET_SIZE - 1), r.y + 1, r.y2, GUI_BLACK); + surfaceLineV(r.x2 - (GADGET_SIZE - 1), r.y + 1, r.y2, GUI_WHITE); + // Prepare font. + fontSet(__guiFontVGA8x8); + fontColorSet(widgetColor, scrollBackgroundColor); + // ***TODO*** + /* + // If we have both horizontal and vertical scroll bars, we need to fix a shadow. + if (w->flags & WIN_SCROLL_V) { + surfaceLineV(w->scrollh.x2 - 1, w->scrollh.y, w->scrollh.y2, GUI_BLACK); + surfaceLineV(w->scrollh.x2, w->scrollh.y, w->scrollh.y2, GUI_WHITE); + } + */ + // Draw arrows. + fontRender("\x11", r.x + 7, r.y + 7); + fontRender("\x10", r.x2 - 12, r.y + 7); + // Distance between arrow buttons on scroll bar. + i = r.x2 - r.x - (GADGET_SIZE * 2 - 2) - 2; + // Percentage to scale content height to scroll bar height. + d = (double)i / (double)(h->max - h->min); + // Find position and size of thumb. + h->thumb.x = r.x + GADGET_SIZE + (h->value * d); + h->thumb.x2 = h->thumb.x + (h->base.r.w * d); + h->thumb.y = r.y + 1; + h->thumb.y2 = r.y2 - 1; + // Clamp overflow due to doubles and my off-by-one brain. + if (h->thumb.x2 >= h->thumb.x + i - 1) h->thumb.x2 = h->thumb.x + i - 1; + // Draw thumb. + surfaceBoxFilled(h->thumb.x + 1, h->thumb.y + 1, h->thumb.x2 - 1, h->thumb.y2 - 1, GUI_LIGHTGRAY); + surfaceBoxHighlight(h->thumb.x, h->thumb.y, h->thumb.x2, h->thumb.y2, GUI_WHITE, GUI_BLACK); + // Adjust thumb values from window space to widget space for click handler. + h->thumb.x -= h->base.r.x; + h->thumb.x2 -= h->base.r.x; + h->thumb.y -= h->base.r.y; + h->thumb.y2 -= h->base.r.y; + } +} + + +void hscrollRangeSet(HscrollT *hscroll, int32_t min, int32_t max) { + hscroll->min = min; + hscroll->max = max; + widgetDirtySet(W(hscroll), 1); +} + + +RegisterT *hscrollRegister(uint8_t magic) { + static RegisterT reg = { + "Hscroll", + hscrollClick, + hscrollDestroy, + hscrollPaint, + NULL // No unregister handler. + }; + + // One-time widget startup code. + __MAGIC_HSCROLL = magic; + + return ® +} + + +int32_t hscrollValueGet(HscrollT *hscroll) { + return hscroll->value; +} + + +void hscrollValueSet(HscrollT *hscroll, int32_t value) { + if (value < hscroll->min) value = hscroll->min; + if (value > hscroll->max) value = hscroll->max; + if (hscroll->value != value) { + hscroll->value = value; + widgetDirtySet(W(hscroll), 1); + } +} diff --git a/roo-e/src/gui/widgets/hscroll.h b/roo-e/src/gui/widgets/hscroll.h new file mode 100644 index 0000000..52dda67 --- /dev/null +++ b/roo-e/src/gui/widgets/hscroll.h @@ -0,0 +1,54 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef HSCROLL_H +#define HSCROLL_H + + +#include "../gui.h" + + +typedef struct HscrollS { + WidgetT base; // Required by all widgets. + int32_t min; + int32_t max; + int32_t value; + RectT thumb; + ClickHandlerT handler; // Actual event handler. +} HscrollT; + + +extern uint8_t __MAGIC_HSCROLL; // Magic ID assigned to us from the GUI. + + +void hscrollClickSet(HscrollT *hscroll, ClickHandlerT handler, void *data); +HscrollT *hscrollCreate(int16_t w, ClickHandlerT handler, void *data, ...); +void hscrollRangeSet(HscrollT *hscroll, int32_t min, int32_t max); +RegisterT *hscrollRegister(uint8_t magic); +int32_t hscrollValueGet(HscrollT *hscroll); +void hscrollValueSet(HscrollT *hscroll, int32_t value); + + +#endif // HSCROLL_H diff --git a/roo-e/src/gui/widgets/label.c b/roo-e/src/gui/widgets/label.c index 4da6b8d..f15c716 100644 --- a/roo-e/src/gui/widgets/label.c +++ b/roo-e/src/gui/widgets/label.c @@ -42,7 +42,7 @@ void labelClickSet(LabelT *label, ClickHandlerT handler, void *data) { void labelColorSet(LabelT *label, ColorT foreground, ColorT background) { label->foreground = foreground; label->background = background; - guiWidgetDirtySet(W(label), 1); + widgetDirtySet(W(label), 1); } @@ -81,7 +81,7 @@ void labelContentSet(LabelT *label, char *content) { label->base.r.y = y; // Fixup width and height. - guiWidgetBaseSet(W(label), __MAGIC_LABEL, width, height); + widgetBaseSet(W(label), __MAGIC_LABEL, width, height); } @@ -98,7 +98,7 @@ LabelT *labelCreate(uint8_t alignment, FontT *font, char *contents, ...) { l->text = strdup(contents); l->pos.x = -1; // Flag for later indicating we need to set up the widget position. - guiWidgetBaseSet(W(l), __MAGIC_LABEL, 0, 0); + widgetBaseSet(W(l), __MAGIC_LABEL, 0, 0); return l; } @@ -127,8 +127,8 @@ static void labelPaint(struct WidgetS *widget, ...) { DEL(temp); } - if (guiWidgetDirtyGet(widget)) { - guiWidgetDirtySet(widget, 0); + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); fontSet(l->font); fontColorSet(l->foreground, l->background); fontModsEnabledSet(l->modsEnabled); diff --git a/roo-e/src/gui/widgets/picture.c b/roo-e/src/gui/widgets/picture.c index cd92e9f..642ca80 100644 --- a/roo-e/src/gui/widgets/picture.c +++ b/roo-e/src/gui/widgets/picture.c @@ -51,7 +51,7 @@ PictureT *pictureCreate(char *filename, ...) { DEL(p); return NULL; } - guiWidgetBaseSet(W(p), __MAGIC_PICTURE, surfaceWidthGet(p->picture), surfaceHeightGet(p->picture)); + widgetBaseSet(W(p), __MAGIC_PICTURE, surfaceWidthGet(p->picture), surfaceHeightGet(p->picture)); return p; } @@ -68,8 +68,8 @@ static void pictureDestroy(struct WidgetS *widget, ...) { static void picturePaint(struct WidgetS *widget, ...) { PictureT *p = (PictureT *)widget; - if (guiWidgetDirtyGet(widget)) { - guiWidgetDirtySet(widget, 0); + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); // ***TODO*** Clipping. surfaceBlit(p->base.r.x, p->base.r.y, 0, 0, 0, 0, p->picture); } diff --git a/roo-e/src/gui/widgets/radio.c b/roo-e/src/gui/widgets/radio.c new file mode 100644 index 0000000..2855bb3 --- /dev/null +++ b/roo-e/src/gui/widgets/radio.c @@ -0,0 +1,210 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#include "../wmwindow.h" +#include "array.h" + +#include "radio.h" + + +uint8_t __MAGIC_RADIO = 0; + + +static void radioClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data); +static void radioDestroy(struct WidgetS *widget, ...); +static void radioPaint(struct WidgetS *widget, ...); + + +static void radioClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) { + RadioT *r = (RadioT *)widget; + + if (event == CLICK_LEFT_UP) { + // Select this radio button. + radioValueSet(r, 1); + + // Call the actual click event if it exists. + if (r->handler) r->handler(widget, x, y, event, data); + + // Repaint. + widgetDirtySet(widget, 1); + } +} + + +void radioClickSet(RadioT *radio, ClickHandlerT handler, void *data) { + radio->handler = handler; + radio->base.data = data; +} + + +RadioT *radioCreate(char *label, uint8_t group, uint8_t value, ...) { + RadioT *r = NULL; + uint16_t width; + uint16_t height; + FontT *f = __guiFontVGA8x16; + + NEW(RadioT, r); + memset(r, 0, sizeof(RadioT)); + + r->group = group; + r->value = value; + r->label = labelCreate(LABEL_ALIGN_LEFT, f, label); + if (!r->label) { + DEL(r); + return NULL; + } + + // Radio button is (height - 3) square. We skip two pixels on the bottom and one on the top to look okay with decender characters. + height = fontHeightGet(f); + width = fontWidthCharactersGet(label) * fontWidthGet(f) + (height * 3); + + widgetBaseSet(W(r), __MAGIC_RADIO, width, height); + + return r; +} + + +static void radioDestroy(struct WidgetS *widget, ...) { + RadioT *r = (RadioT *)widget; + + widgetDestroy(W(r->label)); + DEL(r); +} + + +static void radioPaint(struct WidgetS *widget, ...) { + RadioT *r = (RadioT *)widget; + int16_t w; + int16_t h; + int16_t y; + int16_t x; + int16_t d; + int16_t i; + int16_t change; + ColorT temp; + ColorT top; + ColorT bottom; + ColorT fill; + + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); + // Work out some coordinates. + h = fontHeightGet(r->label->font) - 2; + w = h * 1.5; + x = r->base.r.x + (h * 0.5); // X center coordinate. + change = r->base.r.y + (h * 0.5); // Where, vertically, we start drawing the bottom of the diamond. + d = 1; // Direction of increment. + i = 0; // X increment. + // Set up colors. + if (r->value) { + fill = GUI_DARKGRAY; + top = GUI_BLACK; + bottom = GUI_WHITE; + } else { + fill = GUI_WHITE; + top = GUI_WHITE; + bottom = GUI_BLACK; + } + // Paint radio button. + for (y=r->base.r.y + 1; y<=r->base.r.y + h - 1; y++) { + surfaceLineH(x - i, x + i, y, fill); + surfacePixelSet(x - i, y, top); + surfacePixelSet(x + i, y, top); + if (y == change) { + d = -d; + temp = top; + top = bottom; + bottom = temp; + } + i += d; + } + + // Paint label. + widgetPaintManually(W(r->label), r->base.r.x + w, r->base.r.y); + } +} + + +RegisterT *radioRegister(uint8_t magic) { + static RegisterT reg = { + "Radio", + radioClick, + radioDestroy, + radioPaint, + NULL // No unregister handler. + }; + + // One-time widget startup code. + __MAGIC_RADIO = magic; + + return ® +} + + +uint8_t radioValueGet(RadioT *radio) { + return radio->value; +} + + +void radioValueSet(RadioT *radio, uint8_t value) { + int16_t i; + WindowT *p; + RadioT *r; + + radio->value = (value != 0); + p = radio->base.parent; + if (radio->value) { + // We were selected, deselect other radio buttons in this group. + for (i=0; ichildren); i++) { + // Is this a radio button? Is it not us? + if (p->children[i]->magic == __MAGIC_RADIO && p->children[i] != W(radio)) { + r = (RadioT *)p->children[i]; + // Is it currently selected? + if (r->value && r->group == radio->group) { + // Clear it and redraw. + r->value = 0; + widgetDirtySet(W(r), 1); + break; + } + } + } + } else { + // We were cleared. Select another radio button in this group. + for (i=0; ichildren); i++) { + // Is this a radio button? Is it not us? + if (p->children[i]->magic == __MAGIC_RADIO && p->children[i] != W(radio)) { + r = (RadioT *)p->children[i]; + if (r->group == radio->group) { + // Select it and redraw. + r->value = 1; + widgetDirtySet(W(r), 1); + break; + } + } + } + } + // Repaint. + widgetDirtySet(W(radio), 1); +} diff --git a/roo-e/src/gui/widgets/radio.h b/roo-e/src/gui/widgets/radio.h new file mode 100644 index 0000000..57ea0f4 --- /dev/null +++ b/roo-e/src/gui/widgets/radio.h @@ -0,0 +1,53 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef RADIO_H +#define RADIO_H + + +#include "../gui.h" +#include "label.h" + + +typedef struct RadioS { + WidgetT base; // Required by all widgets. + LabelT *label; // Label to display text. + ClickHandlerT handler; // Actual event handler. + uint8_t value; // Is this radio button selected? + uint8_t group; // Which group does this radio button belong to? +} RadioT; + + +extern uint8_t __MAGIC_RADIO; // Magic ID assigned to us from the GUI. + + +void radioClickSet(RadioT *radio, ClickHandlerT handler, void *data); +RadioT *radioCreate(char *label, uint8_t group, uint8_t value, ...); +RegisterT *radioRegister(uint8_t magic); +uint8_t radioValueGet(RadioT *radio); +void radioValueSet(RadioT *radio, uint8_t value); + + +#endif // RADIO_H diff --git a/roo-e/src/gui/widgets/vscroll.c b/roo-e/src/gui/widgets/vscroll.c new file mode 100644 index 0000000..e171f48 --- /dev/null +++ b/roo-e/src/gui/widgets/vscroll.c @@ -0,0 +1,224 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#include "../wmwindow.h" +#include "../font.h" + +#include "vscroll.h" + + +uint8_t __MAGIC_VSCROLL = 0; + + +static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data); +static void vscrollDestroy(struct WidgetS *widget, ...); +static void vscrollPaint(struct WidgetS *widget, ...); + + +static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) { + VscrollT *v = (VscrollT *)widget; + + // Clicking in up arrow? + if (y <= GADGET_SIZE) { + // Move content up. + v->value -= SCROLL_SPEED_SLOW; + // Clip. + if (v->value < v->min) v->value = v->min; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (v->handler) v->handler(widget, x, y, event, data); + return; + } + + // Clicking in down arrow? + if (y >= v->base.r.h - GADGET_SIZE) { + // Move content down. + v->value += SCROLL_SPEED_SLOW; + // Clip. + if (v->value > v->max - v->base.r.h) v->value = v->max - v->base.r.h; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (v->handler) v->handler(widget, x, y, event, data); + return; + } + + // Clicking above thumb? Also fakes dragging. + if (y < v->thumb.y) { + // Move content up. + v->value -= SCROLL_SPEED_FAST; + // Clip. + if (v->value < v->min) v->value = v->min; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (v->handler) v->handler(widget, x, y, event, data); + return; + } + + // Clicking below thumb? Also fakes dragging. + if (y > v->thumb.y2) { + // Move content down. + v->value += SCROLL_SPEED_FAST; + // Clip. + if (v->value > v->max - v->base.r.h) v->value = v->max - v->base.r.h; + // Update. + widgetDirtySet(widget, 1); + // Call the actual click event. + if (v->handler) v->handler(widget, x, y, event, data); + return; + } +} + + +void vscrollClickSet(VscrollT *vscroll, ClickHandlerT handler, void *data) { + vscroll->handler = handler; + vscroll->base.data = data; +} + + +VscrollT *vscrollCreate(int16_t h, ClickHandlerT handler, void *data, ...) { + VscrollT *v = NULL; + + NEW(VscrollT, v); + memset(v, 0, sizeof(VscrollT)); + + v->min = 1; + v->max = 100; + v->value = 1; + + if (h < GADGET_SIZE * 3) h = GADGET_SIZE * 3; + + widgetBaseSet(W(v), __MAGIC_VSCROLL, GADGET_SIZE, h); + + v->handler = handler; + v->base.data = data; + + widgetInputSetRaw(W(v), 1); + + return v; +} + + +static void vscrollDestroy(struct WidgetS *widget, ...) { + VscrollT *v = (VscrollT *)widget; + + DEL(v); +} + + +static void vscrollPaint(struct WidgetS *widget, ...) { + VscrollT *v = (VscrollT *)widget; + ColorT scrollBackgroundColor; + ColorT widgetColor; + RectT r; + int16_t i; + double d; + + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); + // Find some colors. + if (widgetIsInWindow(widget, wmWindowOnTopGet())) { + scrollBackgroundColor = GUI_DARKGRAY; + widgetColor = GUI_LIGHTGRAY; + } else { + scrollBackgroundColor = GUI_LIGHTGRAY; + widgetColor = GUI_DARKGRAY; + } + // Find coordinates. + r = v->base.r; + r.x2 = r.x + v->base.r.w - 1; + r.y2 = r.y + v->base.r.h - 1; + // Paint scrollbar. + surfaceBoxFilled(r.x + 1, r.y + 1, r.x2 - 1, r.y2 - 1, scrollBackgroundColor); + surfaceBoxHighlight(r.x, r.y, r.x2, r.y2, GUI_WHITE, GUI_BLACK); + surfaceLineH(r.x + 1, r.x2, r.y + (GADGET_SIZE - 1), GUI_BLACK); + surfaceLineH(r.x + 1, r.x2, r.y2 - (GADGET_SIZE - 1), GUI_WHITE); + // Prepare font. + fontSet(__guiFontVGA8x8); + fontColorSet(widgetColor, scrollBackgroundColor); + // Draw arrows. + fontRender("\x1e", r.x + 7, r.y + 7); + fontRender("\x1f", r.x + 7, r.y2 - 12); + // Distance between arrow buttons on scroll bar. + i = r.y2 - r.y - (GADGET_SIZE * 2 - 2) - 2; + // Percentage to scale content height to scroll bar height. + d = (double)i / (double)(v->max - v->min); + // Find position and size of thumb. + v->thumb.x = r.x + 1; + v->thumb.x2 = r.x2 - 1; + v->thumb.y = r.y + GADGET_SIZE + (v->value * d); + v->thumb.y2 = v->thumb.y + (v->base.r.h * d); + // Clamp overflow due to doubles and my off-by-one brain. + if (v->thumb.y2 >= v->thumb.y + i - 1) v->thumb.y2 = v->thumb.y + i - 1; + // Draw thumb. + surfaceBoxFilled(v->thumb.x + 1, v->thumb.y + 1, v->thumb.x2 - 1, v->thumb.y2 - 1, GUI_LIGHTGRAY); + surfaceBoxHighlight(v->thumb.x, v->thumb.y, v->thumb.x2, v->thumb.y2, GUI_WHITE, GUI_BLACK); + // Adjust thumb values from window space to widget space for click handler. + v->thumb.x -= v->base.r.x; + v->thumb.x2 -= v->base.r.x; + v->thumb.y -= v->base.r.y; + v->thumb.y2 -= v->base.r.y; + } +} + + +void vscrollRangeSet(VscrollT *vscroll, int32_t min, int32_t max) { + vscroll->min = min; + vscroll->max = max; + widgetDirtySet(W(vscroll), 1); +} + + +RegisterT *vscrollRegister(uint8_t magic) { + static RegisterT reg = { + "Vscroll", + vscrollClick, + vscrollDestroy, + vscrollPaint, + NULL // No unregister handler. + }; + + // One-time widget startup code. + __MAGIC_VSCROLL = magic; + + return ® +} + + +int32_t vscrollValueGet(VscrollT *vscroll) { + return vscroll->value; +} + + +void vscrollValueSet(VscrollT *vscroll, int32_t value) { + if (value < vscroll->min) value = vscroll->min; + if (value > vscroll->max) value = vscroll->max; + if (vscroll->value != value) { + vscroll->value = value; + widgetDirtySet(W(vscroll), 1); + } +} diff --git a/roo-e/src/gui/widgets/vscroll.h b/roo-e/src/gui/widgets/vscroll.h new file mode 100644 index 0000000..a1363fe --- /dev/null +++ b/roo-e/src/gui/widgets/vscroll.h @@ -0,0 +1,54 @@ +/* + * Roo/E, the Kangaroo Punch Portable GUI Toolkit + * Copyright (C) 2022 Scott Duensing + * + * http://kangaroopunch.com + * + * + * This file is part of Roo/E. + * + * Roo/E is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Roo/E is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Roo/E. If not, see . + * + */ + + +#ifndef VSCROLL_H +#define VSCROLL_H + + +#include "../gui.h" + + +typedef struct VscrollS { + WidgetT base; // Required by all widgets. + int32_t min; + int32_t max; + int32_t value; + RectT thumb; + ClickHandlerT handler; // Actual event handler. +} VscrollT; + + +extern uint8_t __MAGIC_VSCROLL; // Magic ID assigned to us from the GUI. + + +void vscrollClickSet(VscrollT *vscroll, ClickHandlerT handler, void *data); +VscrollT *vscrollCreate(int16_t h, ClickHandlerT handler, void *data, ...); +void vscrollRangeSet(VscrollT *vscroll, int32_t min, int32_t max); +RegisterT *vscrollRegister(uint8_t magic); +int32_t vscrollValueGet(VscrollT *vscroll); +void vscrollValueSet(VscrollT *vscroll, int32_t value); + + +#endif // VSCROLL_H diff --git a/roo-e/src/gui/wmwindow.c b/roo-e/src/gui/wmwindow.c index ec35332..80834eb 100644 --- a/roo-e/src/gui/wmwindow.c +++ b/roo-e/src/gui/wmwindow.c @@ -28,12 +28,6 @@ #include "font.h" -#define ICON_SIZE 32 -#define WIDGET_SIZE 20 -#define SCROLL_SPEED_SLOW 5 // ***TODO*** Move into GUI after we get scrollbar widgets. -#define SCROLL_SPEED_FAST 25 - - uint8_t __MAGIC_WINDOW = 0; @@ -120,11 +114,11 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { if (w->title || w->flags & WIN_CLOSE || w->flags & WIN_MAXIMIZE || w->flags & WIN_MINIMIZE) { tx1 = x1; tx2 = x2; - ty2 = y1 + WIDGET_SIZE; + ty2 = y1 + GADGET_SIZE; // Close box? if (w->flags & WIN_CLOSE) { - tx1 += WIDGET_SIZE; + tx1 += GADGET_SIZE; w->close.x = x1; w->close.y = y1; w->close.x2 = tx1 - 1; @@ -138,10 +132,10 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { // Maximize box? if (w->flags & WIN_MAXIMIZE) { - tx2 -= WIDGET_SIZE; + tx2 -= GADGET_SIZE; w->maximize.x = tx2 + 1; w->maximize.y = y1; - w->maximize.x2 = tx2 + WIDGET_SIZE; + w->maximize.x2 = tx2 + GADGET_SIZE; w->maximize.y2 = ty2 - 1; surfaceBoxFilled(w->maximize.x + 1, w->maximize.y + 1, w->maximize.x2 - 1, w->maximize.y2 - 1, titleBackgroundColor); surfaceLine(w->maximize.x + 4, w->maximize.y + 10, w->maximize.x + 10, w->maximize.y + 4, GUI_WHITE); @@ -155,10 +149,10 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { // Minimize box? if (w->flags & WIN_MINIMIZE) { - tx2 -= WIDGET_SIZE; + tx2 -= GADGET_SIZE; w->minimize.x = tx2 + 1; w->minimize.y = y1; - w->minimize.x2 = tx2 + WIDGET_SIZE; + w->minimize.x2 = tx2 + GADGET_SIZE; w->minimize.y2 = ty2 - 1; surfaceBoxFilled(w->minimize.x + 1, w->minimize.y + 1, w->minimize.x2 - 1, w->minimize.y2 - 1, titleBackgroundColor); surfaceBoxHighlight(w->minimize.x + 7, w->minimize.y + 7, w->minimize.x2 - 7, w->minimize.y2 - 7, GUI_WHITE, widgetColor); @@ -194,7 +188,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { } surfaceBoxHighlight(w->titlebar.x, w->titlebar.y, w->titlebar.x2, w->titlebar.y2, GUI_WHITE, GUI_BLACK); - y1 += WIDGET_SIZE; + y1 += GADGET_SIZE; } else { w->close.x = w->close.y = w->close.x2 = w->close.y2 = 0; w->maximize.x = w->maximize.y = w->maximize.x2 = w->maximize.y2 = 0; @@ -205,14 +199,14 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { // Vertical Scroll Bar? if (w->flags & WIN_SCROLL_V) { w->scrollv.x2 = x2; - w->scrollv.x = w->scrollv.x2 - (WIDGET_SIZE - 1); + w->scrollv.x = w->scrollv.x2 - (GADGET_SIZE - 1); w->scrollv.y = y1; w->scrollv.y2 = y2; // Draw scroll bar. surfaceBoxFilled(w->scrollv.x + 1, w->scrollv.y + 1, w->scrollv.x2 - 1, w->scrollv.y2 - 1, scrollBackgroundColor); surfaceBoxHighlight(w->scrollv.x, w->scrollv.y, w->scrollv.x2, w->scrollv.y2, GUI_WHITE, GUI_BLACK); - surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y + (WIDGET_SIZE - 1), GUI_BLACK); - surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y2 - (WIDGET_SIZE - 1), GUI_WHITE); + surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y + (GADGET_SIZE - 1), GUI_BLACK); + surfaceLineH(w->scrollv.x + 1, w->scrollv.x2, w->scrollv.y2 - (GADGET_SIZE - 1), GUI_WHITE); // Prepare font. fontSet(__guiFontVGA8x8); fontColorSet(widgetColor, scrollBackgroundColor); @@ -220,7 +214,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { fontRender("\x1e", w->scrollv.x + 7, w->scrollv.y + 7); fontRender("\x1f", w->scrollv.x + 7, w->scrollv.y2 - 12); - x2 -= (WIDGET_SIZE - 1); + x2 -= (GADGET_SIZE - 1); } else { w->scrollv.x = w->scrollv.y = w->scrollv.x2 = w->scrollv.y2 = 0; } @@ -230,12 +224,12 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { w->scrollh.x = x1; w->scrollh.x2 = x2; w->scrollh.y2 = y2; - w->scrollh.y = w->scrollh.y2 - (WIDGET_SIZE - 1); + w->scrollh.y = w->scrollh.y2 - (GADGET_SIZE - 1); // Draw scroll bar. surfaceBoxFilled(w->scrollh.x + 1, w->scrollh.y + 1, w->scrollh.x2 - 1, w->scrollh.y2 - 1, scrollBackgroundColor); surfaceBoxHighlight(w->scrollh.x, w->scrollh.y, w->scrollh.x2, w->scrollh.y2, GUI_WHITE, GUI_BLACK); - surfaceLineV(w->scrollh.x + (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_BLACK); - surfaceLineV(w->scrollh.x2 - (WIDGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_WHITE); + surfaceLineV(w->scrollh.x + (GADGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_BLACK); + surfaceLineV(w->scrollh.x2 - (GADGET_SIZE - 1), w->scrollh.y + 1, w->scrollh.y2, GUI_WHITE); // If we have both horizontal and vertical scroll bars, we need to fix a shadow. if (w->flags & WIN_SCROLL_V) { surfaceLineV(w->scrollh.x2 - 1, w->scrollh.y, w->scrollh.y2, GUI_BLACK); @@ -248,7 +242,7 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { fontRender("\x11", w->scrollh.x + 7, w->scrollh.y + 7); fontRender("\x10", w->scrollh.x2 - 12, w->scrollh.y + 7); - y2 -= (WIDGET_SIZE - 1); + y2 -= (GADGET_SIZE - 1); } else { w->scrollh.x = w->scrollh.y = w->scrollh.x2 = w->scrollh.y2 = 0; } @@ -265,13 +259,13 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { // Vertical Scroll Bar Thumb? if (w->flags & WIN_SCROLL_V) { // Distance between arrow buttons on scroll bar. - i = w->scrollv.y2 - w->scrollv.y - (WIDGET_SIZE * 2 - 2) - 2; + i = w->scrollv.y2 - w->scrollv.y - (GADGET_SIZE * 2 - 2) - 2; // Percentage to scale content height to scroll bar height. d = (double)i / (double)surfaceHeightGet(w->content); // Find position and size of thumb. w->thumbv.x = w->scrollv.x + 1; w->thumbv.x2 = w->scrollv.x2 - 1; - w->thumbv.y = w->scrollv.y + WIDGET_SIZE + (w->offset.y * d); + w->thumbv.y = w->scrollv.y + GADGET_SIZE + (w->offset.y * d); w->thumbv.y2 = w->thumbv.y + ((w->bounds.y2 - w->bounds.y) * d); // Clamp overflow due to doubles and my off-by-one brain. if (w->thumbv.y2 >= w->thumbv.y + i - 1) w->thumbv.y2 = w->thumbv.y + i - 1; @@ -285,11 +279,11 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { // Horizontal Scroll Bar Thumb? if (w->flags & WIN_SCROLL_H) { // Distance between arrow buttons on scroll bar. - i = w->scrollh.x2 - w->scrollh.x - (WIDGET_SIZE * 2 - 2) - 2; + i = w->scrollh.x2 - w->scrollh.x - (GADGET_SIZE * 2 - 2) - 2; // Percentage to scale content width to scroll bar width. d = (double)i / (double)surfaceWidthGet(w->content); // Find position and size of thumb. - w->thumbh.x = w->scrollh.x + WIDGET_SIZE + (w->offset.x * d); + w->thumbh.x = w->scrollh.x + GADGET_SIZE + (w->offset.x * d); w->thumbh.x2 = w->thumbh.x + ((w->bounds.x2 - w->bounds.x) * d); w->thumbh.y = w->scrollh.y + 1; w->thumbh.y2 = w->scrollh.y2 - 1; @@ -312,11 +306,11 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { w->resize1.x2 = w->base.r.x + w->base.r.w - 2; w->resize1.x = w->resize1.x2 - 3; w->resize1.y2 = w->base.r.y + w->base.r.h; - w->resize1.y = w->resize1.y2 - (WIDGET_SIZE + 5); + w->resize1.y = w->resize1.y2 - (GADGET_SIZE + 5); surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y, GUI_BLACK); surfaceLineH(w->resize1.x, w->resize1.x2, w->resize1.y + 1, GUI_WHITE); w->resize2.x2 = w->base.r.x + w->base.r.w; - w->resize2.x = w->resize2.x2 - (WIDGET_SIZE + 5); + w->resize2.x = w->resize2.x2 - (GADGET_SIZE + 5); w->resize2.y2 = w->base.r.y + w->base.r.h - 2; w->resize2.y = w->resize2.y2 - 3; surfaceLineV(w->resize2.x, w->resize2.y, w->resize2.y2, GUI_BLACK); @@ -352,7 +346,6 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) { } -#include "image.h" // Passing "flags" as a default int provides proper alignment for the following va_args list. WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...) { int16_t width; @@ -363,7 +356,7 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl NEW(WindowT, win); memset(win, 0, sizeof(WindowT)); - guiWidgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, w, h); + widgetBaseSet((WidgetT *)win, __MAGIC_WINDOW, w, h); win->base.r.x = x; win->base.r.y = y; win->title = strdup(title); @@ -380,8 +373,9 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl windowCache(win, 1); width = win->bounds.x2 - win->bounds.x; height = win->bounds.y2 - win->bounds.y; - guiWidgetDirtySet(W(win), 1); + widgetDirtySet(W(win), 1); } + // Create content surface and clear it. win->content = surfaceCreate(width, height); surfaceSet(win->content); @@ -420,7 +414,7 @@ static void windowDestroy(struct WidgetS *widget, ...) { // Free the title. if (window->title) DEL(window->title); // Free children. - for (c=0; cchildren); c++) guiWidgetDestroy(window->children[c]); + for (c=0; cchildren); c++) widgetDestroy(window->children[c]); arrfree(window->children); // Free cached surface. if (window->cached) surfaceDestroy(&window->cached); @@ -450,7 +444,7 @@ void windowFocusSet(WindowT *win) { } else { // Open window. Use as new top. _windowTop = _windowList[i]; - guiWidgetDirtySet(W(_windowTop), 1); + widgetDirtySet(W(_windowTop), 1); break; } } @@ -471,8 +465,8 @@ void windowFocusSet(WindowT *win) { } // Mark old focus and new focus dirty to repaint title bar. - guiWidgetDirtySet(W(win), 1); - guiWidgetDirtySet(W(_windowTop), 1); + widgetDirtySet(W(win), 1); + widgetDirtySet(W(_windowTop), 1); // Change who has focus. _windowTop = win; @@ -656,7 +650,7 @@ static void windowPaint(struct WidgetS *widget, ...) { // Are any child widgets dirty? y = 0; for (x=0; xchildren); x++) { - if (guiWidgetDirtyGet(w->children[x])) { + if (widgetDirtyGet(w->children[x])) { y = 1; if (w->children[x]->reg->paint) { surfaceSet(w->content); @@ -667,8 +661,8 @@ static void windowPaint(struct WidgetS *widget, ...) { surfaceSet(t); // Does the window itself need redrawn? - if (guiWidgetDirtyGet(widget)) { - guiWidgetDirtySet(widget, 0); + if (widgetDirtyGet(widget)) { + widgetDirtySet(widget, 0); x = 1; } else { x = 0; @@ -723,8 +717,8 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) { int16_t delta; // Too small? - if (width < (WIDGET_SIZE * 4) + 15) width = (WIDGET_SIZE * 4) + 15; - if (height < (WIDGET_SIZE * 4) + 15) height = (WIDGET_SIZE * 4) + 15; + if (width < (GADGET_SIZE * 4) + 15) width = (GADGET_SIZE * 4) + 15; + if (height < (GADGET_SIZE * 4) + 15) height = (GADGET_SIZE * 4) + 15; // Too big? if (win->bounds.w - win->bounds.x + (width - win->base.r.w) + win->offset.x > surfaceWidthGet(win->content)) { @@ -762,7 +756,7 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) { static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { // Clicking in left arrow? - if (e->x <= w->scrollh.x + WIDGET_SIZE) { + if (e->x <= w->scrollh.x + GADGET_SIZE) { // Move content left. w->offset.x -= SCROLL_SPEED_SLOW; // Clip. @@ -773,7 +767,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { } // Clicking in right arrow? - if (e->x >= w->scrollh.x2 - WIDGET_SIZE) { + if (e->x >= w->scrollh.x2 - GADGET_SIZE) { // Move content right. w->offset.x += SCROLL_SPEED_SLOW; // Clip. @@ -810,7 +804,7 @@ static void windowScrollHorizontalHandler(EventT *e, WindowT *w) { static void windowScrollVerticalHandler(EventT *e, WindowT *w) { // Clicking in up arrow? - if (e->y <= w->scrollv.y + WIDGET_SIZE) { + if (e->y <= w->scrollv.y + GADGET_SIZE) { // Move content up. w->offset.y -= SCROLL_SPEED_SLOW; // Clip. @@ -821,7 +815,7 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { } // Clicking in down arrow? - if (e->y >= w->scrollv.y2 - WIDGET_SIZE) { + if (e->y >= w->scrollv.y2 - GADGET_SIZE) { // Move content down. w->offset.y += SCROLL_SPEED_SLOW; // Clip. @@ -856,6 +850,7 @@ static void windowScrollVerticalHandler(EventT *e, WindowT *w) { void windowWidgetAdd(WindowT *window, int16_t x, int16_t y, WidgetT *widget) { + widget->parent = window; widget->r.x = x; widget->r.y = y; arrput(window->children, widget); @@ -877,7 +872,7 @@ void windowWidgetRemove(WindowT *window, WidgetT *widget) { void wmShutdown(void) { uint16_t i; - for (i=0; iflags & WIDGET_RAW_INPUT) && (event->buttons != 0)) { + if (widgetOver->reg->click) { + rawEvent.event = event; + rawEvent.data = widget->data; + widgetOver->reg->click(widgetOver, windowLocalX - widgetOver->r.x, windowLocalY - widgetOver->r.y, CLICK_RAW_INPUT, &rawEvent); + } + return; + } + // Wrap left button processing with a 'for' so we can 'break' out of it. for (;;) { @@ -991,9 +1001,9 @@ void wmUpdate(EventT *event) { } // Are we over a widget? - widget = wuWidgetUnderMouseGet(win, event, &x, &y); + widget = wuWidgetUnderMouseGet(win, event, &windowLocalX, &windowLocalY); if (widget) { - if (widget->reg->click) widget->reg->click(widget, x - widget->r.x, y - widget->r.y, CLICK_LEFT_DOWN, widget->data); + if (widget->reg->click) widget->reg->click(widget, windowLocalX - widget->r.x, windowLocalY - widget->r.y, CLICK_LEFT_DOWN, widget->data); widgetDown = widget; } @@ -1126,9 +1136,9 @@ void wmUpdate(EventT *event) { // Are we still inside the window content? if (event->x <= win->bounds.x2 && event->x >= win->bounds.x && event->y <= win->bounds.y2 && event->y >= win->bounds.y) { // Are we stll over the same widget? - widget = wuWidgetUnderMouseGet(win, event, &x, &y); + widget = wuWidgetUnderMouseGet(win, event, &windowLocalX, &windowLocalY); // Send to widget for processing or send cancel. Convert mouse to widget-local coordinates. - if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, x - widgetDown->r.x, y - widgetDown->r.y, (widget == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data); + if (widgetDown->reg->click) widgetDown->reg->click(widgetDown, windowLocalX - widgetDown->r.x, windowLocalY - widgetDown->r.y, (widget == widgetDown) ? CLICK_LEFT_UP : CLICK_LEFT_CANCEL, widgetDown->data); } widgetDown = NULL; } // widgetDown @@ -1164,3 +1174,8 @@ static WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *loca return NULL; } + + +WindowT *wmWindowOnTopGet(void) { + return _windowTop; +} diff --git a/roo-e/src/gui/wmwindow.h b/roo-e/src/gui/wmwindow.h index 0de63cf..734c0a7 100644 --- a/roo-e/src/gui/wmwindow.h +++ b/roo-e/src/gui/wmwindow.h @@ -30,6 +30,12 @@ #include "gui.h" +#define ICON_SIZE 32 +#define GADGET_SIZE 20 +#define SCROLL_SPEED_SLOW 5 // ***TODO*** Move into GUI after we get scrollbar widgets. +#define SCROLL_SPEED_FAST 25 + + #define WIN_NONE 0 #define WIN_CLOSE 1 #define WIN_MAXIMIZE 2 @@ -81,9 +87,10 @@ void windowWidgetAdd(WindowT *window, int16_t x, int16_t y, WidgetT *widge void windowWidgetRemove(WindowT *window, WidgetT *widget); -void wmShutdown(void); -void wmStartup(void); -void wmUpdate(EventT *event); +void wmShutdown(void); +void wmStartup(void); +void wmUpdate(EventT *event); +WindowT *wmWindowOnTopGet(void); #endif // WMWINDOW_H diff --git a/roo-e/src/main.c b/roo-e/src/main.c index 9f55fef..9f239e3 100644 --- a/roo-e/src/main.c +++ b/roo-e/src/main.c @@ -23,27 +23,42 @@ */ -#include "gui/gui.h" -#include "gui/wmwindow.h" -#include "gui/widgets/label.h" -#include "gui/widgets/picture.h" -#include "gui/widgets/button.h" -#include "gui/widgets/checkbox.h" +#include "gui/gui-all.h" + + +VscrollT *_v = NULL; +VscrollT *_h = NULL; void clickHandler(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) { (void)widget; - logWrite("%d %dx%d %s\n", event, x, y, data); + logWrite("%d %dx%d %s %d %d\n", event, x, y, data, vscrollValueGet(_v), hscrollValueGet(_h)); } int main(int argc, char *argv[]) { char title[256]; uint16_t i; + WindowT *w = NULL; LabelT *l = NULL; ButtonT *b = NULL; CheckboxT *c = NULL; - WindowT *w = NULL; + RadioT *r = NULL; + + + // frame + // scrollarea + + // vscroll + // hscroll + // listbox + // textbox + // up/down + // terminal + // timer + + // msgbox + (void)argc; @@ -66,6 +81,28 @@ int main(int argc, char *argv[]) { c = checkboxCreate("Checkbox", 0); windowWidgetAdd(w, 20, 80, W(c)); + + r = radioCreate("Radio 1", 0, 1); + windowWidgetAdd(w, 20, 110, W(r)); + r = radioCreate("Radio 2", 0, 0); + windowWidgetAdd(w, 120, 110, W(r)); + r = radioCreate("Radio 3", 0, 0); + windowWidgetAdd(w, 220, 110, W(r)); + + r = radioCreate("Radio A", 1, 0); + windowWidgetAdd(w, 20, 140, W(r)); + r = radioCreate("Radio B", 1, 1); + windowWidgetAdd(w, 120, 140, W(r)); + r = radioCreate("Radio C", 1, 0); + windowWidgetAdd(w, 220, 140, W(r)); + + _v = vscrollCreate(100, clickHandler, NULL); + vscrollRangeSet(_v, 0, 640); + windowWidgetAdd(w, 200, 5, W(_v)); + + _h = hscrollCreate(100, clickHandler, NULL); + hscrollRangeSet(_h, 0, 640); + windowWidgetAdd(w, 70, 5, W(_h)); } guiRun(); guiShutdown();