roo_e/roo-e/src/gui/widgets/vscroll.c
2022-07-17 17:31:53 -05:00

226 lines
6 KiB
C

/*
* 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 <https://www.gnu.org/licenses/>.
*
*/
#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;
(void)data;
// 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, v->base.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, v->base.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, v->base.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, v->base.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 (widgetIsInWidget(widget, W(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;
vscrollValueSet(vscroll, min);
}
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 &reg;
}
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);
}
}