244 lines
6.4 KiB
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 ®
|
|
}
|
|
|
|
|
|
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);
|
|
}
|