Lagrange [release]
DocumentWidget: Page load progress indicator
[1mdiff --git a/CMakeLists.txt b/CMakeLists.txt[m
[1mindex b71be6ab..ed064730 100644[m
[1m--- a/CMakeLists.txt[m
[1m+++ b/CMakeLists.txt[m
[36m@@ -129,6 +129,8 @@[m [mset (SOURCES[m
src/ui/command.h[m
src/ui/documentwidget.c[m
src/ui/documentwidget.h[m
[32m+[m[32m src/ui/indicatorwidget.c[m
[32m+[m[32m src/ui/indicatorwidget.h[m
src/ui/listwidget.c[m
src/ui/listwidget.h[m
src/ui/lookupwidget.c[m
[1mdiff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c[m
[1mindex 1f84aed6..bdf7d282 100644[m
[1m--- a/src/ui/documentwidget.c[m
[1m+++ b/src/ui/documentwidget.c[m
[36m@@ -30,6 +30,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
#include "gmrequest.h"[m
#include "gmutil.h"[m
#include "history.h"[m
[32m+[m[32m#include "indicatorwidget.h"[m
#include "inputwidget.h"[m
#include "keys.h"[m
#include "labelwidget.h"[m
[36m@@ -247,6 +248,9 @@[m [mvoid init_DocumentWidget(iDocumentWidget *d) {[m
addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));[m
d->menu = NULL; /* created when clicking */[m
d->playerMenu = NULL;[m
[32m+[m[32m addChildFlags_Widget(w,[m
[32m+[m[32m iClob(new_IndicatorWidget()),[m
[32m+[m[32m resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag);[m
#if !defined (iPlatformApple) /* in system menu */[m
addAction_Widget(w, reload_KeyShortcut, "navigate.reload");[m
addAction_Widget(w, SDLK_w, KMOD_PRIMARY, "tabs.close");[m
[1mdiff --git a/src/ui/indicatorwidget.c b/src/ui/indicatorwidget.c[m
[1mnew file mode 100644[m
[1mindex 00000000..d43e23d9[m
[1m--- /dev/null[m
[1m+++ b/src/ui/indicatorwidget.c[m
[36m@@ -0,0 +1,158 @@[m
[32m+[m[32m/* Copyright 2020 Jaakko Keränen [m
[32m+[m
[32m+[m[32mRedistribution and use in source and binary forms, with or without[m
[32m+[m[32mmodification, are permitted provided that the following conditions are met:[m
[32m+[m
[32m+[m[32m1. Redistributions of source code must retain the above copyright notice, this[m
[32m+[m[32m list of conditions and the following disclaimer.[m
[32m+[m[32m2. Redistributions in binary form must reproduce the above copyright notice,[m
[32m+[m[32m this list of conditions and the following disclaimer in the documentation[m
[32m+[m[32m and/or other materials provided with the distribution.[m
[32m+[m
[32m+[m[32mTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND[m
[32m+[m[32mANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED[m
[32m+[m[32mWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE[m
[32m+[m[32mDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR[m
[32m+[m[32mANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES[m
[32m+[m[32m(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;[m
[32m+[m[32mLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON[m
[32m+[m[32mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m
[32m+[m[32m(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS[m
[32m+[m[32mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
[32m+[m
[32m+[m[32m#include "indicatorwidget.h"[m
[32m+[m[32m#include "paint.h"[m
[32m+[m[32m#include "util.h"[m
[32m+[m[32m#include "app.h"[m
[32m+[m[32m#include "command.h"[m
[32m+[m
[32m+[m[32m#include [m
[32m+[m
[32m+[m[32mstatic int timerId_; /* common timer for all indicators */[m
[32m+[m[32mstatic int animCount_; /* number of animating indicators */[m
[32m+[m
[32m+[m[32mstatic uint32_t postRefresh_(uint32_t interval, void *context) {[m
[32m+[m[32m iUnused(context);[m
[32m+[m[32m postRefresh_App();[m
[32m+[m[32m return interval;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void startTimer_(void) {[m
[32m+[m[32m animCount_++;[m
[32m+[m[32m if (!timerId_) {[m
[32m+[m[32m timerId_ = SDL_AddTimer(1000 / 60, postRefresh_, NULL);[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void stopTimer_(void) {[m
[32m+[m[32m iAssert(animCount_ > 0);[m
[32m+[m[32m if (--animCount_ == 0) {[m
[32m+[m[32m iAssert(timerId_);[m
[32m+[m[32m SDL_RemoveTimer(timerId_);[m
[32m+[m[32m timerId_ = 0;[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstruct Impl_IndicatorWidget{[m
[32m+[m[32m iWidget widget;[m
[32m+[m[32m iAnim pos;[m
[32m+[m[32m};[m
[32m+[m
[32m+[m[32miDefineObjectConstruction(IndicatorWidget)[m
[32m+[m
[32m+[m[32miLocalDef iBool isActive_IndicatorWidget_(const iIndicatorWidget *d) {[m
[32m+[m[32m return isSelected_Widget(d);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void setActive_IndicatorWidget_(iIndicatorWidget *d, iBool set) {[m
[32m+[m[32m setFlags_Widget(as_Widget(d), selected_WidgetFlag, set);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid init_IndicatorWidget(iIndicatorWidget *d) {[m
[32m+[m[32m iWidget *w = &d->widget;[m
[32m+[m[32m init_Widget(w);[m
[32m+[m[32m init_Anim(&d->pos, 0);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void startTimer_IndicatorWidget_(iIndicatorWidget *d) {[m
[32m+[m[32m if (!isActive_IndicatorWidget_(d)) {[m
[32m+[m[32m startTimer_();[m
[32m+[m[32m setActive_IndicatorWidget_(d, iTrue);[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic void stopTimer_IndicatorWidget_(iIndicatorWidget *d) {[m
[32m+[m[32m if (isActive_IndicatorWidget_(d)) {[m
[32m+[m[32m stopTimer_();[m
[32m+[m[32m setActive_IndicatorWidget_(d, iFalse);[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid deinit_IndicatorWidget(iIndicatorWidget *d) {[m
[32m+[m[32m stopTimer_IndicatorWidget_(d);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mstatic iBool isCompleted_IndicatorWidget_(const iIndicatorWidget *d) {[m
[32m+[m[32m return targetValue_Anim(&d->pos) == 1.0f;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid draw_IndicatorWidget_(const iIndicatorWidget *d) {[m
[32m+[m[32m const float pos = value_Anim(&d->pos);[m
[32m+[m[32m if (pos > 0.0f && pos < 1.0f) {[m
[32m+[m[32m const iWidget *w = &d->widget;[m
[32m+[m[32m const iRect rect = innerBounds_Widget(w);[m
[32m+[m[32m iPaint p;[m
[32m+[m[32m init_Paint(&p);[m
[32m+[m[32m drawHLine_Paint(&p,[m
[32m+[m[32m topLeft_Rect(rect),[m
[32m+[m[32m pos * width_Rect(rect),[m
[32m+[m[32m isCompleted_IndicatorWidget_(d) ? uiTextAction_ColorId[m
[32m+[m[32m : uiTextCaution_ColorId);[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32miBool processEvent_IndicatorWidget_(iIndicatorWidget *d, const SDL_Event *ev) {[m
[32m+[m[32m iWidget *w = &d->widget;[m
[32m+[m[32m if (ev->type == SDL_USEREVENT && ev->user.code == refresh_UserEventCode) {[m
[32m+[m[32m if (isFinished_Anim(&d->pos)) {[m
[32m+[m[32m stopTimer_IndicatorWidget_(d);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m else if (isCommand_SDLEvent(ev)) {[m
[32m+[m[32m const char *cmd = command_UserEvent(ev);[m
[32m+[m[32m if (startsWith_CStr(cmd, "document.request.")) {[m
[32m+[m[32m if (pointerLabel_Command(cmd, "doc") == parent_Widget(w)) {[m
[32m+[m[32m cmd += 17;[m
[32m+[m[32m if (equal_Command(cmd, "started")) {[m
[32m+[m[32m setValue_Anim(&d->pos, 0, 0);[m
[32m+[m[32m setValue_Anim(&d->pos, 0.75f, 4000);[m
[32m+[m[32m setFlags_Anim(&d->pos, easeOut_AnimFlag, iTrue);[m
[32m+[m[32m startTimer_IndicatorWidget_(d);[m
[32m+[m[32m }[m
[32m+[m[32m else if (equal_Command(cmd, "finished")) {[m
[32m+[m[32m if (value_Anim(&d->pos) > 0.01f) {[m
[32m+[m[32m setValue_Anim(&d->pos, 1.0f, 250);[m
[32m+[m[32m setFlags_Anim(&d->pos, easeOut_AnimFlag, iFalse);[m
[32m+[m[32m startTimer_IndicatorWidget_(d);[m
[32m+[m[32m }[m
[32m+[m[32m else {[m
[32m+[m[32m setValue_Anim(&d->pos, 0, 0);[m
[32m+[m[32m stopTimer_IndicatorWidget_(d);[m
[32m+[m[32m refresh_Widget(d);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m else if (equal_Command(cmd, "cancelled")) {[m
[32m+[m[32m setValue_Anim(&d->pos, 0, 0);[m
[32m+[m[32m stopTimer_IndicatorWidget_(d);[m
[32m+[m[32m refresh_Widget(d);[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m }[m
[32m+[m[32m return iFalse;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32miBeginDefineSubclass(IndicatorWidget, Widget)[m
[32m+[m[32m .draw = (iAny *) draw_IndicatorWidget_,[m
[32m+[m[32m .processEvent = (iAny *) processEvent_IndicatorWidget_,[m
[32m+[m[32miEndDefineSubclass(IndicatorWidget)[m
[1mdiff --git a/src/ui/indicatorwidget.h b/src/ui/indicatorwidget.h[m
[1mnew file mode 100644[m
[1mindex 00000000..a3d9af39[m
[1m--- /dev/null[m
[1m+++ b/src/ui/indicatorwidget.h[m
[36m@@ -0,0 +1,28 @@[m
[32m+[m[32m/* Copyright 2020 Jaakko Keränen [m
[32m+[m
[32m+[m[32mRedistribution and use in source and binary forms, with or without[m
[32m+[m[32mmodification, are permitted provided that the following conditions are met:[m
[32m+[m
[32m+[m[32m1. Redistributions of source code must retain the above copyright notice, this[m
[32m+[m[32m list of conditions and the following disclaimer.[m
[32m+[m[32m2. Redistributions in binary form must reproduce the above copyright notice,[m
[32m+[m[32m this list of conditions and the following disclaimer in the documentation[m
[32m+[m[32m and/or other materials provided with the distribution.[m
[32m+[m
[32m+[m[32mTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND[m
[32m+[m[32mANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED[m
[32m+[m[32mWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE[m
[32m+[m[32mDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR[m
[32m+[m[32mANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES[m
[32m+[m[32m(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;[m
[32m+[m[32mLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON[m
[32m+[m[32mANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT[m
[32m+[m[32m(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS[m
[32m+[m[32mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */[m
[32m+[m
[32m+[m[32m#pragma once[m
[32m+[m
[32m+[m[32m#include "widget.h"[m
[32m+[m
[32m+[m[32miDeclareWidgetClass(IndicatorWidget)[m
[32m+[m[32miDeclareObjectConstruction(IndicatorWidget)[m
[1mdiff --git a/src/ui/util.c b/src/ui/util.c[m
[1mindex 38124b22..27950c5e 100644[m
[1m--- a/src/ui/util.c[m
[1m+++ b/src/ui/util.c[m
[36m@@ -135,54 +135,15 @@[m [miBool isFinished_Anim(const iAnim *d) {[m
}[m
[m
void init_Anim(iAnim *d, float value) {[m
[31m- d->due = d->when = SDL_GetTicks(); // frameTime_Window(get_Window());[m
[32m+[m[32m d->due = d->when = SDL_GetTicks();[m
d->from = d->to = value;[m
d->flags = 0;[m
}[m
[m
[31m-void setValue_Anim(iAnim *d, float to, uint32_t span) {[m
[31m- if (fabsf(to - d->to) > 0.00001f) {[m
[31m- const uint32_t now = SDL_GetTicks();[m
[31m- d->from = value_Anim(d);[m
[31m- d->to = to;[m
[31m- d->when = now;[m
[31m- d->due = now + span;[m
[31m- }[m
[31m-}[m
[31m-[m
iLocalDef float pos_Anim_(const iAnim *d, uint32_t now) {[m
return (float) (now - d->when) / (float) (d->due - d->when);[m
}[m
[m
[31m-void setValueEased_Anim(iAnim *d, float to, uint32_t span) {[m
[31m- if (fabsf(to - d->to) <= 0.00001f) {[m
[31m- d->to = to; /* Pretty much unchanged. */[m
[31m- return;[m
[31m- }[m
[31m- const uint32_t now = SDL_GetTicks();[m
[31m- if (isFinished_Anim(d)) {[m
[31m- d->from = d->to;[m
[31m- d->when = now;[m
[31m- d->flags = easeBoth_AnimFlag;[m
[31m- }[m
[31m- else {[m
[31m- d->from = value_Anim(d);[m
[31m- d->when = frameTime_Window(get_Window()); /* to match the timing of value_Anim */[m
[31m- d->flags = easeOut_AnimFlag;[m
[31m- }[m
[31m- d->to = to;[m
[31m- d->due = now + span;[m
[31m-}[m
[31m-[m
[31m-void setFlags_Anim(iAnim *d, int flags, iBool set) {[m
[31m- iChangeFlags(d->flags, flags, set);[m
[31m-}[m
[31m-[m
[31m-void stop_Anim(iAnim *d) {[m
[31m- d->from = d->to = value_Anim(d);[m
[31m- d->when = d->due = SDL_GetTicks();[m
[31m-}[m
[31m-[m
iLocalDef float easeIn_(float t) {[m
return t * t;[m
}[m
[36m@@ -198,8 +159,7 @@[m [miLocalDef float easeBoth_(float t) {[m
return 0.5f + easeOut_((t - 0.5f) * 2.0f) * 0.5f;[m
}[m
[m
[31m-float value_Anim(const iAnim *d) {[m
[31m- const uint32_t now = frameTime_Window(get_Window());[m
[32m+[m[32mstatic float valueAt_Anim_(const iAnim *d, const uint32_t now) {[m
if (now >= d->due) {[m
return d->to;[m
}[m
[36m@@ -219,6 +179,52 @@[m [mfloat value_Anim(const iAnim *d) {[m
return d->from * (1.0f - t) + d->to * t;[m
}[m
[m
[32m+[m[32mvoid setValue_Anim(iAnim *d, float to, uint32_t span) {[m
[32m+[m[32m if (span == 0) {[m
[32m+[m[32m d->from = d->to = to;[m
[32m+[m[32m d->when = d->due = SDL_GetTicks();[m
[32m+[m[32m }[m
[32m+[m[32m else if (fabsf(to - d->to) > 0.00001f) {[m
[32m+[m[32m const uint32_t now = SDL_GetTicks();[m
[32m+[m[32m d->from = valueAt_Anim_(d, now);[m
[32m+[m[32m d->to = to;[m
[32m+[m[32m d->when = now;[m
[32m+[m[32m d->due = now + span;[m
[32m+[m[32m }[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid setValueEased_Anim(iAnim *d, float to, uint32_t span) {[m
[32m+[m[32m if (fabsf(to - d->to) <= 0.00001f) {[m
[32m+[m[32m d->to = to; /* Pretty much unchanged. */[m
[32m+[m[32m return;[m
[32m+[m[32m }[m
[32m+[m[32m const uint32_t now = SDL_GetTicks();[m
[32m+[m[32m if (isFinished_Anim(d)) {[m
[32m+[m[32m d->from = d->to;[m
[32m+[m[32m d->flags = easeBoth_AnimFlag;[m
[32m+[m[32m }[m
[32m+[m[32m else {[m
[32m+[m[32m d->from = valueAt_Anim_(d, now);[m
[32m+[m[32m d->flags = easeOut_AnimFlag;[m
[32m+[m[32m }[m
[32m+[m[32m d->to = to;[m
[32m+[m[32m d->when = now;[m
[32m+[m[32m d->due = now + span;[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid setFlags_Anim(iAnim *d, int flags, iBool set) {[m
[32m+[m[32m iChangeFlags(d->flags, flags, set);[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mvoid stop_Anim(iAnim *d) {[m
[32m+[m[32m d->from = d->to = value_Anim(d);[m
[32m+[m[32m d->when = d->due = SDL_GetTicks();[m
[32m+[m[32m}[m
[32m+[m
[32m+[m[32mfloat value_Anim(const iAnim *d) {[m
[32m+[m[32m return valueAt_Anim_(d, frameTime_Window(get_Window()));[m
[32m+[m[32m}[m
[32m+[m
/*-----------------------------------------------------------------------------------------------*/[m
[m
void init_Click(iClick *d, iAnyObject *widget, int button) {[m
[1mdiff --git a/src/ui/widget.h b/src/ui/widget.h[m
[1mindex f39612ed..a1a38f28 100644[m
[1m--- a/src/ui/widget.h[m
[1m+++ b/src/ui/widget.h[m
[36m@@ -160,6 +160,13 @@[m [miLocalDef iObjectList *children_Widget(iAnyObject *d) {[m
iAssert(isInstance_Object(d, &Class_Widget));[m
return ((iWidget *) d)->children;[m
}[m
[32m+[m[32miLocalDef iWidget *parent_Widget(const iAnyObject *d) {[m
[32m+[m[32m if (d) {[m
[32m+[m[32m iAssert(isInstance_Object(d, &Class_Widget));[m
[32m+[m[32m return ((iWidget *) d)->parent;[m
[32m+[m[32m }[m
[32m+[m[32m return NULL;[m
[32m+[m[32m}[m
[m
iBool isVisible_Widget (const iAnyObject *);[m
iBool isDisabled_Widget (const iAnyObject *);[m