I hate scrollbars.
This commit is contained in:
parent
9433a63c1f
commit
814a9ef705
11 changed files with 149 additions and 97 deletions
|
@ -315,7 +315,7 @@ void widgetMove(WidgetT *widget, int16_t x, int16_t y) {
|
||||||
|
|
||||||
|
|
||||||
void widgetPaintManually(WidgetT *widget, int16_t x, int16_t y) {
|
void widgetPaintManually(WidgetT *widget, int16_t x, int16_t y) {
|
||||||
RectT r = widget->r;
|
RectWT r = widget->r;
|
||||||
|
|
||||||
widgetDirtySet(widget, 1);
|
widgetDirtySet(widget, 1);
|
||||||
widget->r.x = x;
|
widget->r.x = x;
|
||||||
|
|
|
@ -59,16 +59,17 @@ typedef struct PointS {
|
||||||
typedef struct RectS {
|
typedef struct RectS {
|
||||||
int16_t x;
|
int16_t x;
|
||||||
int16_t y;
|
int16_t y;
|
||||||
union {
|
|
||||||
int16_t w;
|
|
||||||
int16_t x2;
|
int16_t x2;
|
||||||
};
|
|
||||||
union {
|
|
||||||
int16_t h;
|
|
||||||
int16_t y2;
|
int16_t y2;
|
||||||
};
|
|
||||||
} RectT;
|
} RectT;
|
||||||
|
|
||||||
|
typedef struct RectWS {
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
int16_t w;
|
||||||
|
int16_t h;
|
||||||
|
} RectWT;
|
||||||
|
|
||||||
typedef struct RegisterS {
|
typedef struct RegisterS {
|
||||||
char *widgetName; // Text name of widget.
|
char *widgetName; // Text name of widget.
|
||||||
ClickHandlerT click; // Click handler.
|
ClickHandlerT click; // Click handler.
|
||||||
|
@ -79,7 +80,7 @@ typedef struct RegisterS {
|
||||||
|
|
||||||
typedef struct WidgetS {
|
typedef struct WidgetS {
|
||||||
uint8_t magic; // Magic ID of widget. Must be first!
|
uint8_t magic; // Magic ID of widget. Must be first!
|
||||||
RectT r; // Outer bounds of widget. NOTE: USES WIDTH/HEIGHT, NOT X2/Y2!
|
RectWT r; // Outer bounds of widget. NOTE: USES WIDTH/HEIGHT, NOT X2/Y2!
|
||||||
RegisterT *reg; // Registration information.
|
RegisterT *reg; // Registration information.
|
||||||
void *data; // Pointer to arbitrary data for user.
|
void *data; // Pointer to arbitrary data for user.
|
||||||
uint16_t flags; // Widget flags (see defines above).
|
uint16_t flags; // Widget flags (see defines above).
|
||||||
|
|
|
@ -109,9 +109,9 @@ static void buttonPaint(struct WidgetS *widget, ...) {
|
||||||
if (widgetDirtyGet(widget)) {
|
if (widgetDirtyGet(widget)) {
|
||||||
widgetDirtySet(widget, 0);
|
widgetDirtySet(widget, 0);
|
||||||
// Paint button.
|
// 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, b->base.r.y, b->base.r.x + b->base.r.w - 1, b->base.r.y + b->base.r.h - 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);
|
surfaceBoxHighlight(b->base.r.x + 1, b->base.r.y + 1, b->base.r.x + b->base.r.w - 2, b->base.r.y + b->base.r.h - 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);
|
surfaceBoxFilled(b->base.r.x + 2, b->base.r.y + 2, b->base.r.x + b->base.r.w - 3, b->base.r.y + b->base.r.h - 3, GUI_LIGHTGRAY);
|
||||||
// Paint label.
|
// Paint label.
|
||||||
widgetPaintManually(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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,8 @@ static void hscrollPaint(struct WidgetS *widget, ...) {
|
||||||
widgetColor = GUI_DARKGRAY;
|
widgetColor = GUI_DARKGRAY;
|
||||||
}
|
}
|
||||||
// Find coordinates.
|
// Find coordinates.
|
||||||
r = h->base.r;
|
r.x = h->base.r.x;
|
||||||
|
r.y = h->base.r.y;
|
||||||
r.x2 = r.x + h->base.r.w - 1;
|
r.x2 = r.x + h->base.r.w - 1;
|
||||||
r.y2 = r.y + h->base.r.h - 1;
|
r.y2 = r.y + h->base.r.h - 1;
|
||||||
// Paint scrollbar.
|
// Paint scrollbar.
|
||||||
|
@ -162,14 +163,6 @@ static void hscrollPaint(struct WidgetS *widget, ...) {
|
||||||
// Prepare font.
|
// Prepare font.
|
||||||
fontSet(__guiFontVGA8x8);
|
fontSet(__guiFontVGA8x8);
|
||||||
fontColorSet(widgetColor, scrollBackgroundColor);
|
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.
|
// Draw arrows.
|
||||||
fontRender("\x11", r.x + 7, r.y + 7);
|
fontRender("\x11", r.x + 7, r.y + 7);
|
||||||
fontRender("\x10", r.x2 - 12, r.y + 7);
|
fontRender("\x10", r.x2 - 12, r.y + 7);
|
||||||
|
@ -183,7 +176,7 @@ static void hscrollPaint(struct WidgetS *widget, ...) {
|
||||||
h->thumb.y = r.y + 1;
|
h->thumb.y = r.y + 1;
|
||||||
h->thumb.y2 = r.y2 - 1;
|
h->thumb.y2 = r.y2 - 1;
|
||||||
// Clamp overflow due to doubles and my off-by-one brain.
|
// 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;
|
if (h->thumb.x2 >= r.x + GADGET_SIZE + i - 1) h->thumb.x2 = r.x + GADGET_SIZE + i - 1;
|
||||||
// Draw thumb.
|
// Draw thumb.
|
||||||
surfaceBoxFilled(h->thumb.x + 1, h->thumb.y + 1, h->thumb.x2 - 1, h->thumb.y2 - 1, GUI_LIGHTGRAY);
|
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);
|
surfaceBoxHighlight(h->thumb.x, h->thumb.y, h->thumb.x2, h->thumb.y2, GUI_WHITE, GUI_BLACK);
|
||||||
|
|
|
@ -53,30 +53,33 @@ static void scrollableClickHandler(WidgetT *widget, uint16_t x, uint16_t y, uint
|
||||||
|
|
||||||
|
|
||||||
// Passing "flags" as a default int provides proper alignment for the following va_args list.
|
// Passing "flags" as a default int provides proper alignment for the following va_args list.
|
||||||
ScrollableT *scrollableCreate(int16_t width, int16_t height, int16_t totalWidth, int16_t totalHeight, int flags, ...) {
|
ScrollableT *scrollableCreate(int16_t visibleWidth, int16_t visibleHeight, int16_t totalWidth, int16_t totalHeight, int flags, ...) {
|
||||||
ScrollableT *s = NULL;
|
ScrollableT *s = NULL;
|
||||||
SurfaceT *t = surfaceGet();
|
SurfaceT *t = surfaceGet();
|
||||||
int16_t w = 0;
|
|
||||||
|
|
||||||
NEW(ScrollableT, s);
|
NEW(ScrollableT, s);
|
||||||
memset(s, 0, sizeof(ScrollableT));
|
memset(s, 0, sizeof(ScrollableT));
|
||||||
|
|
||||||
widgetBaseSet(W(s), __MAGIC_SCROLLABLE, width, height);
|
widgetBaseSet(W(s), __MAGIC_SCROLLABLE, visibleWidth, visibleHeight);
|
||||||
|
|
||||||
s->original.x = width;
|
// Remember the original size of the visible area we're painting into because we're going to resize the widget to make room for scroll bars.
|
||||||
s->original.y = height;
|
s->original.x = visibleWidth;
|
||||||
|
s->original.y = visibleHeight;
|
||||||
|
|
||||||
|
logWrite("scrollableCreate: %dx%d\n", s->original.x, s->original.y);
|
||||||
|
|
||||||
// Set up desired scroll bars.
|
// Set up desired scroll bars.
|
||||||
s->flags = flags;
|
s->flags = flags;
|
||||||
if (s->flags & SCROLLABLE_SCROLL_V) {
|
if (s->flags & SCROLLABLE_SCROLL_V) {
|
||||||
|
// Vertical scroll bar.
|
||||||
s->flags |= SCROLLABLE_NOT_SETUP;
|
s->flags |= SCROLLABLE_NOT_SETUP;
|
||||||
s->scrollv = vscrollCreate(height, scrollableClickHandler, s);
|
s->scrollv = vscrollCreate(visibleHeight, scrollableClickHandler, s);
|
||||||
vscrollRangeSet(s->scrollv, 0, totalHeight- 1);
|
vscrollRangeSet(s->scrollv, 0, totalHeight- 1);
|
||||||
w = s->scrollv->base.r.w; // Used when creating horizontal scroll bar below.
|
|
||||||
}
|
}
|
||||||
if (s->flags & SCROLLABLE_SCROLL_H) {
|
if (s->flags & SCROLLABLE_SCROLL_H) {
|
||||||
|
// Horizontal scroll bar.
|
||||||
s->flags |= SCROLLABLE_NOT_SETUP;
|
s->flags |= SCROLLABLE_NOT_SETUP;
|
||||||
s->scrollh = hscrollCreate(width - w, scrollableClickHandler, s);
|
s->scrollh = hscrollCreate(visibleWidth, scrollableClickHandler, s);
|
||||||
hscrollRangeSet(s->scrollh, 0, totalWidth - 1);
|
hscrollRangeSet(s->scrollh, 0, totalWidth - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,38 +113,34 @@ static void scrollableDestroy(struct WidgetS *widget, ...) {
|
||||||
|
|
||||||
|
|
||||||
static void scrollableFixupSizes(ScrollableT *s) {
|
static void scrollableFixupSizes(ScrollableT *s) {
|
||||||
int16_t w = 0;
|
|
||||||
|
|
||||||
// Vertical scrollbar.
|
// Vertical scrollbar.
|
||||||
if (s->scrollv) {
|
if (s->scrollv) {
|
||||||
// Position for vertical scrollbar.
|
// Position for vertical scrollbar. Also used as width of area to blit.
|
||||||
s->sizes.x = s->original.x - s->scrollv->base.r.w;
|
s->position.x = s->original.x - s->scrollv->base.r.w + 1;
|
||||||
// Make scroll area smaller to make room for scrollbar.
|
// Make scroll area smaller to make room for scrollbar.
|
||||||
s->base.r.w = s->original.x - s->scrollv->base.r.w;
|
s->base.r.w = s->original.x - s->scrollv->base.r.w;
|
||||||
// Resize vertical scrollbar.
|
// Resize vertical scrollbar.
|
||||||
vscrollHeightSet(s->scrollv, s->original.y);
|
vscrollHeightSet(s->scrollv, s->original.y - (s->scrollh ? s->scrollh->base.r.h - 1 : 0));
|
||||||
// Position vertical scrollbar.
|
// Position vertical scrollbar.
|
||||||
widgetMove(W(s->scrollv), s->base.r.x + s->sizes.x, s->base.r.y);
|
widgetMove(W(s->scrollv), s->base.r.x + s->position.x, s->base.r.y);
|
||||||
// Used when positioning vertical scrollbar below.
|
|
||||||
w = s->scrollv->base.r.w;
|
|
||||||
} else {
|
} else {
|
||||||
// No vertical scrollbar. Set this to a non-zero value.
|
// No vertical scrollbar. Set this to width of scroll area.
|
||||||
s->sizes.x = s->base.r.w;
|
s->position.x = s->base.r.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Horizontal scrollbar.
|
// Horizontal scrollbar.
|
||||||
if (s->scrollh) {
|
if (s->scrollh) {
|
||||||
// Position for horizontal scrollbar.
|
// Position for horizontal scrollbar. Also used as height of area to blit.
|
||||||
s->sizes.y = s->original.y - s->scrollh->base.r.h;
|
s->position.y = s->original.y - s->scrollh->base.r.h + 1;
|
||||||
// Make scroll area smaller to make room for scrollbar.
|
// Make scroll area smaller to make room for scrollbar.
|
||||||
s->base.r.h = s->original.y - s->scrollh->base.r.h;
|
s->base.r.h = s->original.y - s->scrollh->base.r.h;
|
||||||
// Resize horizontal scrollbar.
|
// Resize horizontal scrollbar.
|
||||||
hscrollWidthSet(s->scrollh, s->original.x - w);
|
hscrollWidthSet(s->scrollh, s->original.x - - (s->scrollv ? s->scrollv->base.r.w - 1: 0));
|
||||||
// Position horizontal scrollbar.
|
// Position horizontal scrollbar.
|
||||||
widgetMove(W(s->scrollh), s->base.r.x, s->base.r.y + s->sizes.y);
|
widgetMove(W(s->scrollh), s->base.r.x, s->base.r.y + s->position.y);
|
||||||
} else {
|
} else {
|
||||||
// No horizontal scrollbar. Set this to a non-zero value.
|
// No horizontal scrollbar. Set this to height of scroll area.
|
||||||
s->sizes.y = s->base.r.h;
|
s->position.y = s->base.r.h;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +166,11 @@ static void scrollablePaint(struct WidgetS *widget, ...) {
|
||||||
scrollableFixupSizes(s);
|
scrollableFixupSizes(s);
|
||||||
if (s->scrollh) {
|
if (s->scrollh) {
|
||||||
if (s->base.flags & WIDGET_IS_WINDOW) s->scrollh->base.flags |= WIDGET_IS_WINDOW;
|
if (s->base.flags & WIDGET_IS_WINDOW) s->scrollh->base.flags |= WIDGET_IS_WINDOW;
|
||||||
widgetAdd(s->base.parent, s->base.r.x, s->base.r.y + s->sizes.y, W(s->scrollh));
|
widgetAdd(s->base.parent, s->base.r.x, s->base.r.y + s->position.y, W(s->scrollh));
|
||||||
}
|
}
|
||||||
if (s->scrollv) {
|
if (s->scrollv) {
|
||||||
if (s->base.flags & WIDGET_IS_WINDOW) s->scrollv->base.flags |= WIDGET_IS_WINDOW;
|
if (s->base.flags & WIDGET_IS_WINDOW) s->scrollv->base.flags |= WIDGET_IS_WINDOW;
|
||||||
widgetAdd(s->base.parent, s->base.r.x + s->sizes.x, s->base.r.y, W(s->scrollv));
|
widgetAdd(s->base.parent, s->base.r.x + s->position.x, s->base.r.y, W(s->scrollv));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +190,7 @@ static void scrollablePaint(struct WidgetS *widget, ...) {
|
||||||
s->base.r.y = o.y;
|
s->base.r.y = o.y;
|
||||||
|
|
||||||
// Blit.
|
// Blit.
|
||||||
|
/*
|
||||||
// if ((s->offset.x + s->sizes.x > surfaceWidthGet(s->area)) || (s->offset.y + s->sizes.y > surfaceHeightGet(s->area))) {
|
// if ((s->offset.x + s->sizes.x > surfaceWidthGet(s->area)) || (s->offset.y + s->sizes.y > surfaceHeightGet(s->area))) {
|
||||||
logWrite("%dx%d %dx%d-%dx%d %dx%d %dx%d %d %d\n",
|
logWrite("%dx%d %dx%d-%dx%d %dx%d %dx%d %d %d\n",
|
||||||
s->base.r.x, s->base.r.y,
|
s->base.r.x, s->base.r.y,
|
||||||
|
@ -200,7 +200,8 @@ static void scrollablePaint(struct WidgetS *widget, ...) {
|
||||||
s->offset.x + s->sizes.x, s->offset.y + s->sizes.y,
|
s->offset.x + s->sizes.x, s->offset.y + s->sizes.y,
|
||||||
hscrollValueGet(s->scrollh), vscrollValueGet(s->scrollv));
|
hscrollValueGet(s->scrollh), vscrollValueGet(s->scrollv));
|
||||||
// }
|
// }
|
||||||
surfaceBlit(s->base.r.x, s->base.r.y, s->offset.x, s->offset.y, s->sizes.x, s->sizes.y, s->area);
|
*/
|
||||||
|
surfaceBlit(s->base.r.x, s->base.r.y, s->offset.x, s->offset.y, s->position.x, s->position.y, s->area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ typedef struct ScrollableS {
|
||||||
SurfaceT *area; // Scrollable area buffer.
|
SurfaceT *area; // Scrollable area buffer.
|
||||||
HscrollT *scrollh; // Horizontal scroll bar.
|
HscrollT *scrollh; // Horizontal scroll bar.
|
||||||
VscrollT *scrollv; // Vertical scroll bar.
|
VscrollT *scrollv; // Vertical scroll bar.
|
||||||
PointT sizes; // Scroll bar width and height info.
|
PointT position; // Horizontan and Veritcal Scroll bar X and Y positions. Also used as area to blit.
|
||||||
PointT original; // Original size of widget before being resized to make room for scollbars.
|
PointT original; // Original size of widget before being resized to make room for scollbars.
|
||||||
uint8_t flags; // Flag bits.
|
uint8_t flags; // Flag bits.
|
||||||
} ScrollableT;
|
} ScrollableT;
|
||||||
|
@ -55,7 +55,7 @@ typedef struct ScrollableS {
|
||||||
extern uint8_t __MAGIC_SCROLLABLE; // Magic ID assigned to us from the GUI.
|
extern uint8_t __MAGIC_SCROLLABLE; // Magic ID assigned to us from the GUI.
|
||||||
|
|
||||||
|
|
||||||
ScrollableT *scrollableCreate(int16_t width, int16_t height, int16_t totalWidth, int16_t totalHeight, int flags, ...);
|
ScrollableT *scrollableCreate(int16_t visibleWidth, int16_t visibleHeight, int16_t totalWidth, int16_t totalHeight, int flags, ...);
|
||||||
void scrollableHeightSet(ScrollableT *scroll, int16_t h);
|
void scrollableHeightSet(ScrollableT *scroll, int16_t h);
|
||||||
RegisterT *scrollableRegister(uint8_t magic);
|
RegisterT *scrollableRegister(uint8_t magic);
|
||||||
void scrollableWidthSet(ScrollableT *scroll, int16_t w);
|
void scrollableWidthSet(ScrollableT *scroll, int16_t w);
|
||||||
|
|
|
@ -35,6 +35,7 @@ uint8_t __MAGIC_VSCROLL = 0;
|
||||||
static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data);
|
static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data);
|
||||||
static void vscrollDestroy(struct WidgetS *widget, ...);
|
static void vscrollDestroy(struct WidgetS *widget, ...);
|
||||||
static void vscrollPaint(struct WidgetS *widget, ...);
|
static void vscrollPaint(struct WidgetS *widget, ...);
|
||||||
|
static void vscrollRecalculate(VscrollT *v);
|
||||||
|
|
||||||
|
|
||||||
static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) {
|
static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event, void *data) {
|
||||||
|
@ -47,7 +48,7 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event,
|
||||||
// Move content up.
|
// Move content up.
|
||||||
v->value -= SCROLL_SPEED_SLOW;
|
v->value -= SCROLL_SPEED_SLOW;
|
||||||
// Clip.
|
// Clip.
|
||||||
if (v->value < v->min) v->value = v->min;
|
if (v->value < 0) v->value = 0;
|
||||||
// Update.
|
// Update.
|
||||||
widgetDirtySet(widget, 1);
|
widgetDirtySet(widget, 1);
|
||||||
// Call the actual click event.
|
// Call the actual click event.
|
||||||
|
@ -60,8 +61,7 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event,
|
||||||
// Move content down.
|
// Move content down.
|
||||||
v->value += SCROLL_SPEED_SLOW;
|
v->value += SCROLL_SPEED_SLOW;
|
||||||
// Clip.
|
// Clip.
|
||||||
logWrite("%d > %d - %d = %d\n", v->value, v->max, v->base.r.h, v->max - v->base.r.h);
|
if (v->value > v->maxValue) v->value = v->maxValue;
|
||||||
if (v->value > v->max - v->base.r.h) v->value = v->max - v->base.r.h;
|
|
||||||
// Update.
|
// Update.
|
||||||
widgetDirtySet(widget, 1);
|
widgetDirtySet(widget, 1);
|
||||||
// Call the actual click event.
|
// Call the actual click event.
|
||||||
|
@ -69,6 +69,7 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// Clicking above thumb? Also fakes dragging.
|
// Clicking above thumb? Also fakes dragging.
|
||||||
if (y < v->thumb.y) {
|
if (y < v->thumb.y) {
|
||||||
// Move content up.
|
// Move content up.
|
||||||
|
@ -94,6 +95,7 @@ static void vscrollClick(WidgetT *widget, uint16_t x, uint16_t y, uint8_t event,
|
||||||
if (v->handler) v->handler(widget, x, y, event, v->base.data);
|
if (v->handler) v->handler(widget, x, y, event, v->base.data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -128,7 +130,6 @@ VscrollT *vscrollCreate(int16_t h, ClickHandlerT handler, void *data, ...) {
|
||||||
|
|
||||||
static void vscrollDestroy(struct WidgetS *widget, ...) {
|
static void vscrollDestroy(struct WidgetS *widget, ...) {
|
||||||
VscrollT *v = (VscrollT *)widget;
|
VscrollT *v = (VscrollT *)widget;
|
||||||
|
|
||||||
DEL(v);
|
DEL(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,9 +137,9 @@ static void vscrollDestroy(struct WidgetS *widget, ...) {
|
||||||
void vscrollHeightSet(VscrollT *vscroll, int16_t h) {
|
void vscrollHeightSet(VscrollT *vscroll, int16_t h) {
|
||||||
// Make sure it's at least tall enough to draw.
|
// Make sure it's at least tall enough to draw.
|
||||||
if (h < GADGET_SIZE * 3) h = GADGET_SIZE * 3;
|
if (h < GADGET_SIZE * 3) h = GADGET_SIZE * 3;
|
||||||
|
|
||||||
vscroll->base.r.h = h;
|
vscroll->base.r.h = h;
|
||||||
|
// This doesn't check that you haven't set some insane value for the height. Up to caller.
|
||||||
|
vscrollRecalculate(vscroll);
|
||||||
widgetDirtySet(W(vscroll), 1);
|
widgetDirtySet(W(vscroll), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +149,6 @@ static void vscrollPaint(struct WidgetS *widget, ...) {
|
||||||
ColorT scrollBackgroundColor;
|
ColorT scrollBackgroundColor;
|
||||||
ColorT widgetColor;
|
ColorT widgetColor;
|
||||||
RectT r;
|
RectT r;
|
||||||
int16_t i;
|
|
||||||
double d;
|
|
||||||
|
|
||||||
if (widgetDirtyGet(widget)) {
|
if (widgetDirtyGet(widget)) {
|
||||||
widgetDirtySet(widget, 0);
|
widgetDirtySet(widget, 0);
|
||||||
|
@ -161,8 +160,11 @@ static void vscrollPaint(struct WidgetS *widget, ...) {
|
||||||
scrollBackgroundColor = GUI_LIGHTGRAY;
|
scrollBackgroundColor = GUI_LIGHTGRAY;
|
||||||
widgetColor = GUI_DARKGRAY;
|
widgetColor = GUI_DARKGRAY;
|
||||||
}
|
}
|
||||||
// Find coordinates.
|
// Do the math.
|
||||||
r = v->base.r;
|
vscrollRecalculate(v);
|
||||||
|
// Find coordinates to draw scrollbar.
|
||||||
|
r.x = v->base.r.x;
|
||||||
|
r.y = v->base.r.y;
|
||||||
r.x2 = r.x + v->base.r.w - 1;
|
r.x2 = r.x + v->base.r.w - 1;
|
||||||
r.y2 = r.y + v->base.r.h - 1;
|
r.y2 = r.y + v->base.r.h - 1;
|
||||||
// Paint scrollbar.
|
// Paint scrollbar.
|
||||||
|
@ -176,17 +178,6 @@ static void vscrollPaint(struct WidgetS *widget, ...) {
|
||||||
// Draw arrows.
|
// Draw arrows.
|
||||||
fontRender("\x1e", r.x + 7, r.y + 7);
|
fontRender("\x1e", r.x + 7, r.y + 7);
|
||||||
fontRender("\x1f", r.x + 7, r.y2 - 12);
|
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.
|
// Draw thumb.
|
||||||
surfaceBoxFilled(v->thumb.x + 1, v->thumb.y + 1, v->thumb.x2 - 1, v->thumb.y2 - 1, GUI_LIGHTGRAY);
|
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);
|
surfaceBoxHighlight(v->thumb.x, v->thumb.y, v->thumb.x2, v->thumb.y2, GUI_WHITE, GUI_BLACK);
|
||||||
|
@ -206,6 +197,48 @@ void vscrollRangeSet(VscrollT *vscroll, int32_t min, int32_t max) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void vscrollRecalculate(VscrollT *v) {
|
||||||
|
int16_t range;
|
||||||
|
int16_t areaTop;
|
||||||
|
int16_t area;
|
||||||
|
int16_t areaBottom;
|
||||||
|
int16_t thumbSize;
|
||||||
|
float percent;
|
||||||
|
RectT r;
|
||||||
|
|
||||||
|
// Range of scroll.
|
||||||
|
range = v->max - v->min + 1;
|
||||||
|
|
||||||
|
// Find coordinates to draw scrollbar. Setting r to be one smaller than the widget's size makes later math cleaner.
|
||||||
|
r.x = v->base.r.x;
|
||||||
|
r.y = v->base.r.y;
|
||||||
|
r.x2 = r.x + v->base.r.w - 1;
|
||||||
|
r.y2 = r.y + v->base.r.h - 1;
|
||||||
|
|
||||||
|
// Find coordinates of thumb area.
|
||||||
|
areaTop = r.y + GADGET_SIZE + 1;
|
||||||
|
areaBottom = r.y2 - GADGET_SIZE - 1;
|
||||||
|
area = areaBottom - areaTop + 1;
|
||||||
|
|
||||||
|
// Find scale percentage.
|
||||||
|
percent = (float)area / (float)range;
|
||||||
|
|
||||||
|
// Find thumb position and size.
|
||||||
|
thumbSize = (int16_t)(percent * (float)area);
|
||||||
|
v->thumb.x = r.x + 1;
|
||||||
|
v->thumb.x2 = r.x2 - 1;
|
||||||
|
v->thumb.y = (int16_t)((float)v->value * percent) + areaTop;
|
||||||
|
v->thumb.y2 = v->thumb.y + thumbSize;
|
||||||
|
v->maxValue = (area - thumbSize) / percent;
|
||||||
|
|
||||||
|
// Clamp.
|
||||||
|
logWrite("maxValue %d + area %d >= range %d\n", v->maxValue, area, range);
|
||||||
|
//if (v->maxValue + area >= range) v->maxValue = range - area - 1;
|
||||||
|
if (v->maxValue < 0) v->maxValue = 0;
|
||||||
|
if (v->value > v->maxValue) vscrollValueSet(v, v->maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
RegisterT *vscrollRegister(uint8_t magic) {
|
RegisterT *vscrollRegister(uint8_t magic) {
|
||||||
static RegisterT reg = {
|
static RegisterT reg = {
|
||||||
"Vscroll",
|
"Vscroll",
|
||||||
|
@ -223,15 +256,17 @@ RegisterT *vscrollRegister(uint8_t magic) {
|
||||||
|
|
||||||
|
|
||||||
int32_t vscrollValueGet(VscrollT *vscroll) {
|
int32_t vscrollValueGet(VscrollT *vscroll) {
|
||||||
return vscroll->value;
|
// Internal value is always 0 based, so adjust for min.
|
||||||
|
return vscroll->value + vscroll->min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void vscrollValueSet(VscrollT *vscroll, int32_t value) {
|
void vscrollValueSet(VscrollT *vscroll, int32_t value) {
|
||||||
|
// Clamp.
|
||||||
if (value < vscroll->min) value = vscroll->min;
|
if (value < vscroll->min) value = vscroll->min;
|
||||||
if (value > vscroll->max) value = vscroll->max;
|
if (value > vscroll->max) value = vscroll->max;
|
||||||
if (vscroll->value != value) {
|
// Internal value is always 0 based, so adjust for min.
|
||||||
vscroll->value = value;
|
vscroll->value = value - vscroll->min;
|
||||||
|
vscrollRecalculate(vscroll);
|
||||||
widgetDirtySet(W(vscroll), 1);
|
widgetDirtySet(W(vscroll), 1);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ typedef struct VscrollS {
|
||||||
int32_t min;
|
int32_t min;
|
||||||
int32_t max;
|
int32_t max;
|
||||||
int32_t value;
|
int32_t value;
|
||||||
|
int32_t maxValue;
|
||||||
RectT thumb;
|
RectT thumb;
|
||||||
ClickHandlerT handler; // Actual event handler.
|
ClickHandlerT handler; // Actual event handler.
|
||||||
} VscrollT;
|
} VscrollT;
|
||||||
|
|
|
@ -235,8 +235,8 @@ static void windowCache(WindowT *w, uint8_t redrawWindow) {
|
||||||
|
|
||||||
// Passing "flags" as a default int provides proper alignment for the following va_args list.
|
// 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, ...) {
|
WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *title, int flags, ...) {
|
||||||
int16_t width;
|
PointT visibleSize;
|
||||||
int16_t height;
|
PointT totalSize;
|
||||||
va_list args;
|
va_list args;
|
||||||
WindowT *win = NULL;
|
WindowT *win = NULL;
|
||||||
int8_t sflags = SCROLLABLE_NONE;
|
int8_t sflags = SCROLLABLE_NONE;
|
||||||
|
@ -249,24 +249,29 @@ WindowT *windowCreate(uint16_t x, uint16_t y, uint16_t w, uint16_t h, char *titl
|
||||||
win->title = strdup(title);
|
win->title = strdup(title);
|
||||||
win->flags = (uint8_t)flags;
|
win->flags = (uint8_t)flags;
|
||||||
|
|
||||||
|
logWrite("windowCreate: %dx%d\n", win->base.r.w, win->base.r.h);
|
||||||
|
|
||||||
|
// Cache the window so we get valid bounds values.
|
||||||
|
windowCache(win, 1);
|
||||||
|
visibleSize.x = win->bounds.x2 - win->bounds.x + 1;
|
||||||
|
visibleSize.y = win->bounds.y2 - win->bounds.y + 1;
|
||||||
|
|
||||||
// If the window is resizable, we need to get two more arguments for the content size.
|
// If the window is resizable, we need to get two more arguments for the content size.
|
||||||
if (win->flags & WIN_RESIZE) {
|
if (win->flags & WIN_RESIZE) {
|
||||||
va_start(args, flags);
|
va_start(args, flags);
|
||||||
width = va_arg(args, int);
|
totalSize.x = va_arg(args, int);
|
||||||
height = va_arg(args, int);
|
totalSize.y = va_arg(args, int);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
sflags = SCROLLABLE_STANDARD;
|
sflags = SCROLLABLE_STANDARD;
|
||||||
} else {
|
} else {
|
||||||
// Use whatever the default content area is. This causes an extra draw on create. Oh well.
|
// Use whatever the default content area is. This causes an extra draw on create. Oh well.
|
||||||
windowCache(win, 1);
|
totalSize = visibleSize;
|
||||||
width = win->bounds.x2 - win->bounds.x;
|
|
||||||
height = win->bounds.y2 - win->bounds.y;
|
|
||||||
widgetDirtySet(W(win), 1);
|
widgetDirtySet(W(win), 1);
|
||||||
if (flags & WIN_SCROLL_H) sflags |= SCROLLABLE_SCROLL_H;
|
if (flags & WIN_SCROLL_H) sflags |= SCROLLABLE_SCROLL_H;
|
||||||
if (flags & WIN_SCROLL_V) sflags |= SCROLLABLE_SCROLL_V;
|
if (flags & WIN_SCROLL_V) sflags |= SCROLLABLE_SCROLL_V;
|
||||||
}
|
}
|
||||||
|
|
||||||
win->scroll = scrollableCreate(w, h, width, height, sflags);
|
win->scroll = scrollableCreate(visibleSize.x, visibleSize.y, totalSize.x, totalSize.y, sflags);
|
||||||
win->scroll->base.flags |= WIDGET_IS_WINDOW;
|
win->scroll->base.flags |= WIDGET_IS_WINDOW;
|
||||||
widgetAdd(W(win), 0, 0, W(win->scroll));
|
widgetAdd(W(win), 0, 0, W(win->scroll));
|
||||||
|
|
||||||
|
@ -561,11 +566,18 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) {
|
||||||
int16_t y = 0;
|
int16_t y = 0;
|
||||||
int16_t content = 0;
|
int16_t content = 0;
|
||||||
int16_t change = 0;
|
int16_t change = 0;
|
||||||
|
PointT chrome;
|
||||||
|
|
||||||
|
// Find size of window chrome.
|
||||||
|
chrome.x = win->base.r.w - (win->bounds.x2 - win->bounds.x) + 1;
|
||||||
|
chrome.y = win->base.r.h - (win->bounds.y2 - win->bounds.y) + 1;
|
||||||
|
|
||||||
// Too small?
|
// Too small?
|
||||||
if (width < (GADGET_SIZE * 4) + 15) width = (GADGET_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;
|
if (height < (GADGET_SIZE * 4) + chrome.y) height = (GADGET_SIZE * 4) + chrome.y;
|
||||||
|
// logWrite("Chrome Y: %d Height: %d\n", chrome.y, height);
|
||||||
|
|
||||||
|
/*
|
||||||
// Too big? Horizontal.
|
// Too big? Horizontal.
|
||||||
// Get the current view offset.
|
// Get the current view offset.
|
||||||
x = scrollableValueHGet(win->scroll);
|
x = scrollableValueHGet(win->scroll);
|
||||||
|
@ -590,22 +602,28 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) {
|
||||||
// Clamp.
|
// Clamp.
|
||||||
x = win->base.r.w - content + surfaceWidthGet(win->scroll->area);
|
x = win->base.r.w - content + surfaceWidthGet(win->scroll->area);
|
||||||
if (width > x) width = x;
|
if (width > x) width = x;
|
||||||
|
*/
|
||||||
|
|
||||||
// Too big? Vertical.
|
// Too big? Vertical.
|
||||||
// Get the current view offset.
|
// Get the current view offset.
|
||||||
y = scrollableValueVGet(win->scroll);
|
y = scrollableValueVGet(win->scroll);
|
||||||
// Height of content area of window.
|
// Height of content area of window.
|
||||||
content = win->bounds.y2 - win->bounds.y;
|
content = win->bounds.y2 - win->bounds.y + 1;
|
||||||
|
// Do we have a vertical scroll bar?
|
||||||
if (win->scroll->scrollv) {
|
if (win->scroll->scrollv) {
|
||||||
// Do we need to take the height of the horizontal scroll bar into account?
|
// Do we need to take the height of the horizontal scroll bar into account?
|
||||||
if (win->scroll->scrollh) content -= win->scroll->scrollh->base.r.h;
|
if (win->scroll->scrollh) content -= win->scroll->scrollh->base.r.h;
|
||||||
// Change in height.
|
// Change in height.
|
||||||
change = height - win->base.r.h;
|
change = height - win->base.r.h;
|
||||||
|
// Is the visible content area with this change offset by the scrollbar taller than the total content area?
|
||||||
if (content + change + y > surfaceHeightGet(win->scroll->area)) {
|
if (content + change + y > surfaceHeightGet(win->scroll->area)) {
|
||||||
// Do we have room to scroll content into view?
|
// Do we have room to scroll content into view?
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
|
// Find out how far we're trying to scroll.
|
||||||
delta = height - win->base.r.h;
|
delta = height - win->base.r.h;
|
||||||
|
// Clamp to available scroll area.
|
||||||
if (delta > y) delta = y;
|
if (delta > y) delta = y;
|
||||||
|
// Scroll.
|
||||||
scrollableValueVSet(win->scroll, y - delta);
|
scrollableValueVSet(win->scroll, y - delta);
|
||||||
}
|
}
|
||||||
height = (win->base.r.h - content - y + surfaceHeightGet(win->scroll->area));
|
height = (win->base.r.h - content - y + surfaceHeightGet(win->scroll->area));
|
||||||
|
@ -613,6 +631,7 @@ void windowResize(WindowT *win, uint16_t width, uint16_t height) {
|
||||||
}
|
}
|
||||||
// Clamp.
|
// Clamp.
|
||||||
y = win->base.r.h - content + surfaceHeightGet(win->scroll->area);
|
y = win->base.r.h - content + surfaceHeightGet(win->scroll->area);
|
||||||
|
// logWrite("Y Clamp: %d > %d Chrome: %d\n", height, y, win->base.r.h - content);
|
||||||
if (height > y) height = y;
|
if (height > y) height = y;
|
||||||
|
|
||||||
// Did the size change?
|
// Did the size change?
|
||||||
|
@ -690,7 +709,7 @@ void wmUpdate(EventT *event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're over a widget that has raw input processing enabled and we haven't started a click on another widget yet, send to target for raw processing.
|
// If we're over a widget that has raw input processing enabled and we haven't started a click on another widget yet, send to target for raw processing.
|
||||||
if (widgetOver && !widgetDown && !dragging && (widgetOver->flags & WIDGET_RAW_INPUT) && (event->buttons != 0)) {
|
if (widgetOver && !widgetDown && !dragging && !resizing && (widgetOver->flags & WIDGET_RAW_INPUT) && (event->buttons != 0)) {
|
||||||
if (widgetOver->reg->click) {
|
if (widgetOver->reg->click) {
|
||||||
rawEvent.event = event;
|
rawEvent.event = event;
|
||||||
rawEvent.data = widget->data;
|
rawEvent.data = widget->data;
|
||||||
|
@ -720,7 +739,7 @@ void wmUpdate(EventT *event) {
|
||||||
// DEBUG - draw active regions.
|
// DEBUG - draw active regions.
|
||||||
surfaceSet(__guiBackBuffer);
|
surfaceSet(__guiBackBuffer);
|
||||||
surfaceBox(win->base.r.x, win->base.r.y, x2, y2, GUI_YELLOW);
|
surfaceBox(win->base.r.x, win->base.r.y, x2, y2, GUI_YELLOW);
|
||||||
surfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_CYAN);
|
surfaceBox(win->bounds.x, win->bounds.y, win->bounds.x2, win->bounds.y2, GUI_YELLOW);
|
||||||
surfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_LIGHTBLUE);
|
surfaceBox(win->close.x, win->close.y, win->close.x2, win->close.y2, GUI_LIGHTBLUE);
|
||||||
surfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_LIGHTCYAN);
|
surfaceBox(win->titlebar.x, win->titlebar.y, win->titlebar.x2, win->titlebar.y2, GUI_LIGHTCYAN);
|
||||||
surfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_LIGHTGREEN);
|
surfaceBox(win->minimize.x, win->minimize.y, win->minimize.x2, win->minimize.y2, GUI_LIGHTGREEN);
|
||||||
|
@ -916,7 +935,7 @@ static WidgetT *wuWidgetUnderMouseGet(WindowT *win, EventT *event, int16_t *loca
|
||||||
// Find widget under mouse.
|
// Find widget under mouse.
|
||||||
for (i=0; i<arrlen(win->base.children); i++) {
|
for (i=0; i<arrlen(win->base.children); i++) {
|
||||||
widget = win->base.children[i];
|
widget = win->base.children[i];
|
||||||
if (*localX >= widget->r.x && *localX < widget->r.x + widget->r.x2 && *localY >= widget->r.y && *localY < widget->r.y + widget->r.y2) {
|
if (*localX >= widget->r.x && *localX < widget->r.x + widget->r.w && *localY >= widget->r.y && *localY < widget->r.y + widget->r.h) {
|
||||||
//logWrite("Over %s\n", widget->reg->widgetName);
|
//logWrite("Over %s\n", widget->reg->widgetName);
|
||||||
// Return this widget.
|
// Return this widget.
|
||||||
return widget;
|
return widget;
|
||||||
|
|
|
@ -60,7 +60,7 @@ typedef struct WindowS {
|
||||||
RectT resize1; // First resize area.
|
RectT resize1; // First resize area.
|
||||||
RectT resize2; // Second resize area.
|
RectT resize2; // Second resize area.
|
||||||
ScrollableT *scroll; // Window contents and scroll bars.
|
ScrollableT *scroll; // Window contents and scroll bars.
|
||||||
RectT restore; // Size of window if they restore from maximized.
|
RectWT restore; // Size of window if they restore from maximized.
|
||||||
PointT restoreOffset; // Scroll position if they restore from maximized.
|
PointT restoreOffset; // Scroll position if they restore from maximized.
|
||||||
RectT bounds; // Inside edge of window frame.
|
RectT bounds; // Inside edge of window frame.
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -66,7 +66,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
if (guiStartup(800, 600, 16) == SUCCESS) {
|
if (guiStartup(800, 600, 16) == SUCCESS) {
|
||||||
|
|
||||||
for (i=1; i<4; i++) {
|
for (i=1; i<2; i++) {
|
||||||
sprintf(title, "Testing %d", i);
|
sprintf(title, "Testing %d", i);
|
||||||
w = windowCreate(i * 50, i * 50, 600, 400, title, WIN_STANDARD, 640, 480);
|
w = windowCreate(i * 50, i * 50, 600, 400, title, WIN_STANDARD, 640, 480);
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ int main(int argc, char *argv[]) {
|
||||||
r = radioCreate("Radio C", 1, 0);
|
r = radioCreate("Radio C", 1, 0);
|
||||||
widgetAdd(W(w), 220, 140, W(r));
|
widgetAdd(W(w), 220, 140, W(r));
|
||||||
|
|
||||||
|
/*
|
||||||
_v = vscrollCreate(100, clickHandler, NULL);
|
_v = vscrollCreate(100, clickHandler, NULL);
|
||||||
vscrollRangeSet(_v, 0, 640);
|
vscrollRangeSet(_v, 0, 640);
|
||||||
widgetAdd(W(w), 200, 5, W(_v));
|
widgetAdd(W(w), 200, 5, W(_v));
|
||||||
|
@ -105,6 +106,7 @@ int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
s = scrollableCreate(300, 200, 648, 480, SCROLLABLE_STANDARD);
|
s = scrollableCreate(300, 200, 648, 480, SCROLLABLE_STANDARD);
|
||||||
widgetAdd(W(w), 20, 170, W(s));
|
widgetAdd(W(w), 20, 170, W(s));
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
guiRun();
|
guiRun();
|
||||||
guiShutdown();
|
guiShutdown();
|
||||||
|
|
Loading…
Add table
Reference in a new issue