From fa174461abdc5c33de16428109c7d46b4f150093 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 18 Mar 2021 11:26:11 +0200 Subject: Scrollbar fading and periodic commands Added a new mechanism to issue periodic but not per-frame commands. This is used for main-thread operations like checking if it's time to fade away the scrollbars. Scrollbars are faded away completely on Apple platforms. Adjusted list right margins accordingly. --- CMakeLists.txt | 2 + src/app.c | 29 +++++++++++--- src/app.h | 2 + src/periodic.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ src/periodic.h | 39 ++++++++++++++++++ src/ui/listwidget.c | 10 +++-- src/ui/scrollwidget.c | 85 ++++++++++++++++++++++++++++++++++++--- src/ui/sidebarwidget.c | 11 +++-- src/ui/util.c | 3 ++ src/ui/util.h | 1 - src/ui/widget.c | 19 ++++++--- src/ui/widget.h | 31 +++++++------- src/ui/window.c | 2 +- 13 files changed, 301 insertions(+), 40 deletions(-) create mode 100644 src/periodic.c create mode 100644 src/periodic.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 51cad4b2..8c248738 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,8 @@ set (SOURCES src/media.h src/mimehooks.c src/mimehooks.h + src/periodic.c + src/periodic.h src/prefs.c src/prefs.h src/stb_image.h diff --git a/src/app.c b/src/app.c index 383b9e2a..c5ddc454 100644 --- a/src/app.c +++ b/src/app.c @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "gmutil.h" #include "history.h" #include "ipc.h" +#include "periodic.h" #include "ui/certimportwidget.h" #include "ui/color.h" #include "ui/command.h" @@ -108,7 +109,7 @@ struct Impl_App { iVisited * visited; iBookmarks * bookmarks; iWindow * window; - iSortedArray tickers; + iSortedArray tickers; /* per-frame callbacks, used for animations */ uint32_t lastTickerTime; uint32_t elapsedSinceLastTicker; iBool isRunning; @@ -123,6 +124,7 @@ struct Impl_App { iBool isFinishedLaunching; iTime lastDropTime; /* for detecting drops of multiple items */ int autoReloadTimer; + iPeriodic periodic; /* Preferences: */ iBool commandEcho; /* --echo */ iBool forceSoftwareRender; /* --sw */ @@ -132,6 +134,8 @@ struct Impl_App { static iApp app_; +/*----------------------------------------------------------------------------------------------*/ + iDeclareType(Ticker) struct Impl_Ticker { @@ -144,6 +148,8 @@ static int cmp_Ticker_(const void *a, const void *b) { return iCmp(elems[0]->context, elems[1]->context); } +/*----------------------------------------------------------------------------------------------*/ + const iString *dateStr_(const iDate *date) { return collectNewFormat_String("%d-%02d-%02d %02d:%02d:%02d", date->year, @@ -611,6 +617,7 @@ static void init_App_(iApp *d, int argc, char **argv) { d->visited = new_Visited(); d->bookmarks = new_Bookmarks(); d->tabEnum = 0; /* generates unique IDs for tab pages */ + init_Periodic(&d->periodic); setThemePalette_Color(d->prefs.theme); #if defined (iPlatformAppleDesktop) setupApplication_MacOS(); @@ -668,6 +675,10 @@ static void init_App_(iApp *d, int argc, char **argv) { } static void deinit_App(iApp *d) { +#if defined (LAGRANGE_IDLE_SLEEP) + SDL_RemoveTimer(d->sleepTimer); +#endif + SDL_RemoveTimer(d->autoReloadTimer); saveState_App_(d); deinit_Feeds(); save_Keys(dataDir_App_()); @@ -681,13 +692,14 @@ static void deinit_App(iApp *d) { delete_GmCerts(d->certs); save_MimeHooks(d->mimehooks); delete_MimeHooks(d->mimehooks); - deinit_SortedArray(&d->tickers); delete_Window(d->window); d->window = NULL; deinit_CommandLine(&d->args); iRelease(d->launchCommands); delete_String(d->execPath); deinit_Ipc(); + deinit_SortedArray(&d->tickers); + deinit_Periodic(&d->periodic); iRecycle(); } @@ -892,10 +904,11 @@ void processEvents_App(enum iAppEventMode eventMode) { default: { #if defined (LAGRANGE_IDLE_SLEEP) if (ev.type == SDL_USEREVENT && ev.user.code == asleep_UserEventCode) { - if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_) { + if (SDL_GetTicks() - d->lastEventTime > idleThreshold_App_ && + isEmpty_SortedArray(&d->tickers)) { if (!d->isIdling) { // printf("[App] idling...\n"); - fflush(stdout); +// fflush(stdout); } d->isIdling = iTrue; } @@ -904,7 +917,7 @@ void processEvents_App(enum iAppEventMode eventMode) { d->lastEventTime = SDL_GetTicks(); if (d->isIdling) { // printf("[App] ...woke up\n"); - fflush(stdout); +// fflush(stdout); } d->isIdling = iFalse; #endif @@ -1043,9 +1056,9 @@ void refresh_App(void) { #if defined (LAGRANGE_IDLE_SLEEP) if (d->isIdling) return; #endif + set_Atomic(&d->pendingRefresh, iFalse); destroyPending_Widget(); draw_Window(d->window); - set_Atomic(&d->pendingRefresh, iFalse); } iBool isRefreshPending_App(void) { @@ -1175,6 +1188,10 @@ iMimeHooks *mimeHooks_App(void) { return app_.mimehooks; } +iPeriodic *periodic_App(void) { + return &app_.periodic; +} + iBool isLandscape_App(void) { const iInt2 size = rootSize_Window(get_Window()); return size.x > size.y; diff --git a/src/app.h b/src/app.h index bdb0e22f..4d09f5da 100644 --- a/src/app.h +++ b/src/app.h @@ -35,6 +35,7 @@ iDeclareType(Bookmarks) iDeclareType(DocumentWidget) iDeclareType(GmCerts) iDeclareType(MimeHooks) +iDeclareType(Periodic) iDeclareType(Visited) iDeclareType(Window) @@ -78,6 +79,7 @@ iGmCerts * certs_App (void); iVisited * visited_App (void); iBookmarks * bookmarks_App (void); iMimeHooks * mimeHooks_App (void); +iPeriodic * periodic_App (void); iDocumentWidget * document_App (void); iObjectList * listDocuments_App (void); iDocumentWidget * newTab_App (const iDocumentWidget *duplicateOf, iBool switchToNew); diff --git a/src/periodic.c b/src/periodic.c new file mode 100644 index 00000000..c840e63c --- /dev/null +++ b/src/periodic.c @@ -0,0 +1,107 @@ +/* Copyright 2021 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "periodic.h" +#include "app.h" + +#include +#include + +iDeclareType(PeriodicCommand) + +struct Impl_PeriodicCommand { + iAny * context; + iString command; +}; + +static void init_PeriodicCommand(iPeriodicCommand *d, iAny *context, const char *command) { + d->context = context; + initCStr_String(&d->command, command); +} + +static void deinit_PeriodicCommand(iPeriodicCommand *d) { + deinit_String(&d->command); +} + +static int cmp_PeriodicCommand_(const void *a, const void *b) { + const iPeriodicCommand *elems[2] = { a, b }; + return iCmp(elems[0]->context, elems[1]->context); +} + +iDefineTypeConstructionArgs(PeriodicCommand, (iAny *ctx, const char *cmd), ctx, cmd) + +/*----------------------------------------------------------------------------------------------*/ + +static uint32_t postCommands_Periodic_(uint32_t interval, void *param) { + iPeriodic *d = param; + lock_Mutex(d->mutex); + iConstForEach(Array, i, &d->commands.values) { + postCommandString_App(&((const iPeriodicCommand *) i.value)->command); + } + unlock_Mutex(d->mutex); + return interval; +} + +void init_Periodic(iPeriodic *d) { + d->mutex = new_Mutex(); + init_SortedArray(&d->commands, sizeof(iPeriodicCommand), cmp_PeriodicCommand_); + d->timer = SDL_AddTimer(500, postCommands_Periodic_, d); +} + +void deinit_Periodic(iPeriodic *d) { + SDL_RemoveTimer(d->timer); + iGuardMutex(d->mutex, { + iForEach(Array, i, &d->commands.values) { + deinit_PeriodicCommand(i.value); + } + deinit_SortedArray(&d->commands); + }); + delete_Mutex(d->mutex); +} + +void add_Periodic(iPeriodic *d, iAny *context, const char *command) { + lock_Mutex(d->mutex); + size_t pos; + iPeriodicCommand key = { .context = context }; + if (locate_SortedArray(&d->commands, &key, &pos)) { + iPeriodicCommand *pc = at_SortedArray(&d->commands, pos); + setCStr_String(&pc->command, command); + } + else { + iPeriodicCommand pc; + init_PeriodicCommand(&pc, context, command); + insert_SortedArray(&d->commands, &pc); + } + unlock_Mutex(d->mutex); +} + +void remove_Periodic(iPeriodic *d, iAny *context) { + lock_Mutex(d->mutex); + size_t pos; + iPeriodicCommand key = { .context = context }; + if (locate_SortedArray(&d->commands, &key, &pos)) { + iPeriodicCommand *pc = at_SortedArray(&d->commands, pos); + deinit_PeriodicCommand(pc); + remove_Array(&d->commands.values, pos); + } + unlock_Mutex(d->mutex); +} diff --git a/src/periodic.h b/src/periodic.h new file mode 100644 index 00000000..acb00aa2 --- /dev/null +++ b/src/periodic.h @@ -0,0 +1,39 @@ +/* Copyright 2021 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include +#include + +iDeclareType(Periodic) + +/* Animation utility. Not per frame but several times per second. */ +struct Impl_Periodic { + int timer; + iMutex * mutex; + iSortedArray commands; +}; + +void init_Periodic (iPeriodic *); +void deinit_Periodic (iPeriodic *); + +void add_Periodic (iPeriodic *, iAny *context, const char *command); +void remove_Periodic (iPeriodic *, iAny *context); diff --git a/src/ui/listwidget.c b/src/ui/listwidget.c index 237562ca..f351c3b3 100644 --- a/src/ui/listwidget.c +++ b/src/ui/listwidget.c @@ -379,9 +379,13 @@ static void draw_ListWidget_(const iListWidget *d) { beginTarget_Paint(&p, buf->texture); fillRect_Paint(&p, (iRect){ zero_I2(), d->visBuf->texSize }, bg[i]); } - const iRect sbBlankRect = - { init_I2(d->visBuf->texSize.x - scrollBarWidth_ListWidget(d), 0), - init_I2(scrollBarWidth_ListWidget(d), d->itemHeight) }; +#if defined (iPlatformApple) + const int blankWidth = 0; /* scrollbars fade away */ +#else + const int blankWidth = scrollBarWidth_ListWidget(d); +#endif + const iRect sbBlankRect = { init_I2(d->visBuf->texSize.x - blankWidth, 0), + init_I2(blankWidth, d->itemHeight) }; iConstForEach(IntSet, v, &d->invalidItems) { const size_t index = *v.value; if (contains_Range(&drawItems, index)) { diff --git a/src/ui/scrollwidget.c b/src/ui/scrollwidget.c index e887ddbf..a08b58d7 100644 --- a/src/ui/scrollwidget.c +++ b/src/ui/scrollwidget.c @@ -23,9 +23,23 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "scrollwidget.h" #include "paint.h" #include "util.h" +#include "periodic.h" +#include "app.h" + +#include iDefineObjectConstruction(ScrollWidget) +static float minOpacity_(void) { +#if !defined (iPlatformApple) + if (deviceType_App() == desktop_AppDeviceType) { + /* Don't fade the scrollbars completely. */ + return 0.333f; + } +#endif + return 0.0f; +} + struct Impl_ScrollWidget { iWidget widget; iRangei range; @@ -33,12 +47,23 @@ struct Impl_ScrollWidget { int thumbSize; iClick click; int startThumb; + iAnim opacity; + uint32_t fadeStart; + iBool willCheckFade; }; static void updateMetrics_ScrollWidget_(iScrollWidget *d) { as_Widget(d)->rect.size.x = gap_UI * 3; } +static void animateOpacity_ScrollWidget_(void *ptr) { + iScrollWidget *d = ptr; + if (!isFinished_Anim(&d->opacity)) { + addTicker_App(animateOpacity_ScrollWidget_, ptr); + } + refresh_Widget(ptr); +} + void init_ScrollWidget(iScrollWidget *d) { iWidget *w = as_Widget(d); init_Widget(w); @@ -49,10 +74,13 @@ void init_ScrollWidget(iScrollWidget *d) { iTrue); updateMetrics_ScrollWidget_(d); init_Click(&d->click, d, SDL_BUTTON_LEFT); + init_Anim(&d->opacity, minOpacity_()); + d->willCheckFade = iFalse; } void deinit_ScrollWidget(iScrollWidget *d) { - iUnused(d); + remove_Periodic(periodic_App(), d); + removeTicker_App(animateOpacity_ScrollWidget_, d); } static int thumbSize_ScrollWidget_(const iScrollWidget *d) { @@ -74,10 +102,26 @@ static iRect thumbRect_ScrollWidget_(const iScrollWidget *d) { return rect; } +static void unfade_ScrollWidget_(iScrollWidget *d, float opacity) { + d->fadeStart = SDL_GetTicks() + 1000; + if (targetValue_Anim(&d->opacity) < opacity) { + setValue_Anim(&d->opacity, opacity, 66); + addTicker_App(animateOpacity_ScrollWidget_, d); + } + if (!d->willCheckFade) { + d->willCheckFade = iTrue; + add_Periodic(periodic_App(), d, "scrollbar.fade"); + } + refresh_Widget(d); +} + static void checkVisible_ScrollWidget_(iScrollWidget *d) { - setFlags_Widget(as_Widget(d), - hidden_WidgetFlag, - d->thumbSize != 0 ? height_Rect(thumbRect_ScrollWidget_(d)) == 0 : iTrue); + const iBool wasHidden = isVisible_Widget(d); + const iBool isHidden = d->thumbSize != 0 ? height_Rect(thumbRect_ScrollWidget_(d)) == 0 : iTrue; + setFlags_Widget(as_Widget(d), hidden_WidgetFlag, isHidden); + if (wasHidden && !isHidden) { + unfade_ScrollWidget_(d, 1.0f); + } } void setRange_ScrollWidget(iScrollWidget *d, iRangei range) { @@ -87,9 +131,13 @@ void setRange_ScrollWidget(iScrollWidget *d, iRangei range) { } void setThumb_ScrollWidget(iScrollWidget *d, int thumb, int thumbSize) { + const int oldThumb = d->thumb; d->thumb = thumb; d->thumbSize = thumbSize; checkVisible_ScrollWidget_(d); + if (oldThumb != d->thumb && thumbSize) { + unfade_ScrollWidget_(d, 1.0f); + } } static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { @@ -97,6 +145,25 @@ static iBool processEvent_ScrollWidget_(iScrollWidget *d, const SDL_Event *ev) { if (isMetricsChange_UserEvent(ev)) { updateMetrics_ScrollWidget_(d); } + if (ev->type == SDL_MOUSEMOTION) { + const iInt2 mouse = init_I2(ev->motion.x, ev->motion.y); + const iBool isNearby = containsExpanded_Widget(&d->widget, mouse, 4 * gap_UI); + const iBool isOver = isNearby && contains_Rect(thumbRect_ScrollWidget_(d), mouse); + if (isNearby) { + unfade_ScrollWidget_(d, isOver ? 1.0f : 0.4f); + } + } + if (isCommand_UserEvent(ev, "scrollbar.fade")) { + if (d->willCheckFade && SDL_GetTicks() > d->fadeStart) { + setValue_Anim(&d->opacity, minOpacity_(), 200); + remove_Periodic(periodic_App(), d); + d->willCheckFade = iFalse; + if (!isFinished_Anim(&d->opacity)) { + addTicker_App(animateOpacity_ScrollWidget_, d); + } + } + return iFalse; + } switch (processEvent_Click(&d->click, ev)) { case started_ClickResult: setFlags_Widget(w, pressed_WidgetFlag, iTrue); @@ -146,10 +213,18 @@ static void draw_ScrollWidget_(const iScrollWidget *d) { if (bounds.size.x > 0) { iPaint p; init_Paint(&p); - //drawVLine_Paint(&p, topLeft_Rect(bounds), height_Rect(bounds), uiSeparator_ColorId); + /* Blend if opacity is not at maximum. */ + p.alpha = 255 * value_Anim(&d->opacity); + SDL_Renderer *render = renderer_Window(get_Window()); + if (p.alpha < 255) { + SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_BLEND); + } const iRect thumbRect = shrunk_Rect( thumbRect_ScrollWidget_(d), init_I2(isPressed ? gap_UI : (gap_UI * 4 / 3), gap_UI / 2)); fillRect_Paint(&p, thumbRect, isPressed ? uiBackgroundPressed_ColorId : tmQuote_ColorId); + if (p.alpha < 255) { + SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE); + } } } diff --git a/src/ui/sidebarwidget.c b/src/ui/sidebarwidget.c index 802669c7..ac119575 100644 --- a/src/ui/sidebarwidget.c +++ b/src/ui/sidebarwidget.c @@ -143,7 +143,7 @@ static void updateItems_SidebarWidget_(iSidebarWidget *d) { iDate on; initCurrent_Time(&now); init_Date(&on, &now); - const int thisYear = on.year; + const iDate today = on; iZap(on); size_t numItems = 0; iConstForEach(PtrArray, i, listEntries_Feeds()) { @@ -1306,6 +1306,11 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, const iBool isHover = isHover_Widget(constAs_Widget(list)) && constHoverItem_ListWidget(list) == d; const int scrollBarWidth = scrollBarWidth_ListWidget(list); +#if defined (iPlatformApple) + const int blankWidth = 0; +#else + const int blankWidth = scrollBarWidth; +#endif const int itemHeight = height_Rect(itemRect); const int iconColor = isHover ? (isPressing ? uiTextPressed_ColorId : uiIconHover_ColorId) : uiIcon_ColorId; @@ -1345,7 +1350,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, if (d != constItem_ListWidget(list, 0)) { drawHLine_Paint(p, addY_I2(pos, 2 * gap_UI), - width_Rect(itemRect) - scrollBarWidth, + width_Rect(itemRect) - blankWidth, uiSeparator_ColorId); } drawRange_Text( @@ -1463,7 +1468,7 @@ static void draw_SidebarItem_(const iSidebarItem *d, iPaint *p, iRect itemRect, iInt2 drawPos = addY_I2(topLeft_Rect(itemRect), d->id); drawHLine_Paint(p, addY_I2(drawPos, -gap_UI), - width_Rect(itemRect) - scrollBarWidth, + width_Rect(itemRect) - blankWidth, uiSeparator_ColorId); drawRange_Text( uiLabelLargeBold_FontId, diff --git a/src/ui/util.c b/src/ui/util.c index e07e036a..13d1bf78 100644 --- a/src/ui/util.c +++ b/src/ui/util.c @@ -401,6 +401,7 @@ static iBool isCommandIgnoredByMenus_(const char *cmd) { equal_Command(cmd, "document.request.updated") || equal_Command(cmd, "document.request.finished") || equal_Command(cmd, "document.changed") || + equal_Command(cmd, "scrollbar.fade") || equal_Command(cmd, "visited.changed") || (deviceType_App() == desktop_AppDeviceType && equal_Command(cmd, "window.resized")) || equal_Command(cmd, "widget.overflow") || @@ -1634,12 +1635,14 @@ void updateValueInput_Widget(iWidget *d, const char *title, const char *prompt) static iBool messageHandler_(iWidget *msg, const char *cmd) { /* Almost any command dismisses the sheet. */ + /* TODO: Use a "notification" prefix (like `) to ignore all types of commands line this? */ if (!(equal_Command(cmd, "media.updated") || equal_Command(cmd, "media.player.update") || equal_Command(cmd, "bookmarks.request.finished") || equal_Command(cmd, "document.autoreload") || equal_Command(cmd, "document.reload") || equal_Command(cmd, "document.request.updated") || + equal_Command(cmd, "scrollbar.fade") || equal_Command(cmd, "widget.overflow") || startsWith_CStr(cmd, "window."))) { destroy_Widget(msg); diff --git a/src/ui/util.h b/src/ui/util.h index 35e10c6f..09e52a4d 100644 --- a/src/ui/util.h +++ b/src/ui/util.h @@ -102,7 +102,6 @@ struct Impl_Anim { void init_Anim (iAnim *, float value); void setValue_Anim (iAnim *, float to, uint32_t span); -void setValueLinear_Anim (iAnim *, float to, uint32_t span); void setValueEased_Anim (iAnim *, float to, uint32_t span); void setFlags_Anim (iAnim *, int flags, iBool set); void stop_Anim (iAnim *); diff --git a/src/ui/widget.c b/src/ui/widget.c index d5e639fd..4f5d174c 100644 --- a/src/ui/widget.c +++ b/src/ui/widget.c @@ -582,10 +582,17 @@ iInt2 localCoord_Widget(const iWidget *d, iInt2 coord) { } iBool contains_Widget(const iWidget *d, iInt2 coord) { - const iRect bounds = { zero_I2(), addY_I2(d->rect.size, - d->flags & drawBackgroundToBottom_WidgetFlag ? - rootSize_Window(get_Window()).y : 0) }; - return contains_Rect(bounds, localCoord_Widget(d, coord)); + return containsExpanded_Widget(d, coord, 0); +} + +iBool containsExpanded_Widget(const iWidget *d, iInt2 coord, int expand) { + const iRect bounds = { + zero_I2(), + addY_I2(d->rect.size, + d->flags & drawBackgroundToBottom_WidgetFlag ? rootSize_Window(get_Window()).y : 0) + }; + return contains_Rect(expand ? expanded_Rect(bounds, init1_I2(expand)) : bounds, + localCoord_Widget(d, coord)); } iLocalDef iBool isKeyboardEvent_(const SDL_Event *ev) { @@ -854,8 +861,8 @@ void drawBackground_Widget(const iWidget *d) { const iBool isLight = isLight_ColorTheme(colorTheme_App()); p.alpha = isLight ? 0xc : 0x20; SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_BLEND); - iRect shadowRect = expanded_Rect(bounds_Widget(d), mulf_I2(gap2_UI, 1)); - shadowRect.pos.y += gap_UI / 4; + iRect shadowRect = expanded_Rect(bounds_Widget(d), mulf_I2(gap2_UI, 8)); +// shadowRect.pos.y += gap_UI * 4; fillRect_Paint(&p, shadowRect, /*isLight ? white_ColorId :*/ black_ColorId); SDL_SetRenderDrawBlendMode(renderer_Window(get_Window()), SDL_BLENDMODE_NONE); } diff --git a/src/ui/widget.h b/src/ui/widget.h index 88d75b62..90ccac8e 100644 --- a/src/ui/widget.h +++ b/src/ui/widget.h @@ -161,21 +161,22 @@ void destroy_Widget (iWidget *); /* widget removed and deleted later void destroyPending_Widget (void); void releaseChildren_Widget (iWidget *); -const iString *id_Widget (const iWidget *); -int64_t flags_Widget (const iWidget *); -iRect bounds_Widget (const iWidget *); /* outer bounds */ -iRect innerBounds_Widget (const iWidget *); -iInt2 localCoord_Widget (const iWidget *, iInt2 coord); -iBool contains_Widget (const iWidget *, iInt2 coord); -iAny * hitChild_Widget (const iWidget *, iInt2 coord); -iAny * findChild_Widget (const iWidget *, const char *id); -const iPtrArray *findChildren_Widget (const iWidget *, const char *id); -iAny * findParentClass_Widget(const iWidget *, const iAnyClass *class); -iAny * findFocusable_Widget(const iWidget *startFrom, enum iWidgetFocusDir focusDir); -size_t childCount_Widget (const iWidget *); -void draw_Widget (const iWidget *); -void drawBackground_Widget(const iWidget *); -void drawChildren_Widget (const iWidget *); +const iString *id_Widget (const iWidget *); +int64_t flags_Widget (const iWidget *); +iRect bounds_Widget (const iWidget *); /* outer bounds */ +iRect innerBounds_Widget (const iWidget *); +iInt2 localCoord_Widget (const iWidget *, iInt2 coord); +iBool contains_Widget (const iWidget *, iInt2 coord); +iBool containsExpanded_Widget (const iWidget *, iInt2 coord, int expand); +iAny * hitChild_Widget (const iWidget *, iInt2 coord); +iAny * findChild_Widget (const iWidget *, const char *id); +const iPtrArray *findChildren_Widget (const iWidget *, const char *id); +iAny * findParentClass_Widget (const iWidget *, const iAnyClass *class); +iAny * findFocusable_Widget (const iWidget *startFrom, enum iWidgetFocusDir focusDir); +size_t childCount_Widget (const iWidget *); +void draw_Widget (const iWidget *); +void drawBackground_Widget (const iWidget *); +void drawChildren_Widget (const iWidget *); iLocalDef int width_Widget(const iAnyObject *d) { if (d) { diff --git a/src/ui/window.c b/src/ui/window.c index 1c0c0394..e7945c62 100644 --- a/src/ui/window.c +++ b/src/ui/window.c @@ -1827,7 +1827,7 @@ void draw_Window(iWindow *d) { } } #endif - const int winFlags = SDL_GetWindowFlags(d->win); + const int winFlags = SDL_GetWindowFlags(d->win); const iBool gotFocus = (winFlags & SDL_WINDOW_INPUT_FOCUS) != 0; /* Clear the window. The clear color is visible as a border around the window when the custom frame is being used. */ { -- cgit v1.2.3