roo_e/roo-e/src/gui/widgets/hscroll.c

244 lines
6.4 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 "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;
(void)data;
// 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, h->base.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, h->base.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, h->base.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, h->base.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;
widgetBaseSet(W(h), __MAGIC_HSCROLL, w, GADGET_SIZE);
hscrollWidthSet(h, w);
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 (widgetIsInWidget(widget, W(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;
hscrollValueSet(hscroll, min);
}
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 &reg;
}
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);
}
}
void hscrollWidthSet(HscrollT *hscroll, int16_t w) {
// Make sure it's at least wide enough to draw.
if (w < GADGET_SIZE * 3) w = GADGET_SIZE * 3;
hscroll->base.r.w = w;
widgetDirtySet(W(hscroll), 1);
}