Lagrange [work/v1.17]

DocumentWidget: Page load progress indicator

=> 859fad2c6d5013ace7fcb749b591468dd0b65612

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b71be6ab..ed064730 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -129,6 +129,8 @@ set (SOURCES
     src/ui/command.h
     src/ui/documentwidget.c
     src/ui/documentwidget.h
+    src/ui/indicatorwidget.c
+    src/ui/indicatorwidget.h
     src/ui/listwidget.c
     src/ui/listwidget.h
     src/ui/lookupwidget.c
diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c
index 1f84aed6..bdf7d282 100644
--- a/src/ui/documentwidget.c
+++ b/src/ui/documentwidget.c
@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
 #include "gmrequest.h"
 #include "gmutil.h"
 #include "history.h"
+#include "indicatorwidget.h"
 #include "inputwidget.h"
 #include "keys.h"
 #include "labelwidget.h"
@@ -247,6 +248,9 @@ void init_DocumentWidget(iDocumentWidget *d) {
     addChild_Widget(w, iClob(d->scroll = new_ScrollWidget()));
     d->menu = NULL; /* created when clicking */
     d->playerMenu = NULL;
+    addChildFlags_Widget(w,
+                         iClob(new_IndicatorWidget()),
+                         resizeToParentWidth_WidgetFlag | resizeToParentHeight_WidgetFlag);
 #if !defined (iPlatformApple) /* in system menu */
     addAction_Widget(w, reload_KeyShortcut, "navigate.reload");
     addAction_Widget(w, SDLK_w, KMOD_PRIMARY, "tabs.close");
diff --git a/src/ui/indicatorwidget.c b/src/ui/indicatorwidget.c
new file mode 100644
index 00000000..d43e23d9
--- /dev/null
+++ b/src/ui/indicatorwidget.c
@@ -0,0 +1,158 @@
+/* Copyright 2020 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 "indicatorwidget.h"
+#include "paint.h"
+#include "util.h"
+#include "app.h"
+#include "command.h"
+
+#include 
+
+static int timerId_;   /* common timer for all indicators */
+static int animCount_; /* number of animating indicators */
+
+static uint32_t postRefresh_(uint32_t interval, void *context) {
+    iUnused(context);
+    postRefresh_App();
+    return interval;
+}
+
+static void startTimer_(void) {
+    animCount_++;
+    if (!timerId_) {
+        timerId_ = SDL_AddTimer(1000 / 60, postRefresh_, NULL);
+    }
+}
+
+static void stopTimer_(void) {
+    iAssert(animCount_ > 0);
+    if (--animCount_ == 0) {
+        iAssert(timerId_);
+        SDL_RemoveTimer(timerId_);
+        timerId_ = 0;
+    }
+}
+
+struct Impl_IndicatorWidget{
+    iWidget widget;
+    iAnim   pos;
+};
+
+iDefineObjectConstruction(IndicatorWidget)
+
+iLocalDef iBool isActive_IndicatorWidget_(const iIndicatorWidget *d) {
+    return isSelected_Widget(d);
+}
+
+static void setActive_IndicatorWidget_(iIndicatorWidget *d, iBool set) {
+    setFlags_Widget(as_Widget(d), selected_WidgetFlag, set);
+}
+
+void init_IndicatorWidget(iIndicatorWidget *d) {
+    iWidget *w = &d->widget;
+    init_Widget(w);
+    init_Anim(&d->pos, 0);
+}
+
+static void startTimer_IndicatorWidget_(iIndicatorWidget *d) {
+    if (!isActive_IndicatorWidget_(d)) {
+        startTimer_();
+        setActive_IndicatorWidget_(d, iTrue);
+    }
+}
+
+static void stopTimer_IndicatorWidget_(iIndicatorWidget *d) {
+    if (isActive_IndicatorWidget_(d)) {
+        stopTimer_();
+        setActive_IndicatorWidget_(d, iFalse);
+    }
+}
+
+void deinit_IndicatorWidget(iIndicatorWidget *d) {
+    stopTimer_IndicatorWidget_(d);
+}
+
+static iBool isCompleted_IndicatorWidget_(const iIndicatorWidget *d) {
+    return targetValue_Anim(&d->pos) == 1.0f;
+}
+
+void draw_IndicatorWidget_(const iIndicatorWidget *d) {
+    const float pos = value_Anim(&d->pos);
+    if (pos > 0.0f && pos < 1.0f) {
+        const iWidget *w = &d->widget;
+        const iRect rect = innerBounds_Widget(w);
+        iPaint p;
+        init_Paint(&p);
+        drawHLine_Paint(&p,
+                        topLeft_Rect(rect),
+                        pos * width_Rect(rect),
+                        isCompleted_IndicatorWidget_(d) ? uiTextAction_ColorId
+                                                        : uiTextCaution_ColorId);
+    }
+}
+
+iBool processEvent_IndicatorWidget_(iIndicatorWidget *d, const SDL_Event *ev) {
+    iWidget *w = &d->widget;
+    if (ev->type == SDL_USEREVENT && ev->user.code == refresh_UserEventCode) {
+        if (isFinished_Anim(&d->pos)) {
+            stopTimer_IndicatorWidget_(d);
+        }
+    }
+    else if (isCommand_SDLEvent(ev)) {
+        const char *cmd = command_UserEvent(ev);
+        if (startsWith_CStr(cmd, "document.request.")) {
+            if (pointerLabel_Command(cmd, "doc") == parent_Widget(w)) {
+                cmd += 17;
+                if (equal_Command(cmd, "started")) {
+                    setValue_Anim(&d->pos, 0, 0);
+                    setValue_Anim(&d->pos, 0.75f, 4000);
+                    setFlags_Anim(&d->pos, easeOut_AnimFlag, iTrue);
+                    startTimer_IndicatorWidget_(d);
+                }
+                else if (equal_Command(cmd, "finished")) {
+                    if (value_Anim(&d->pos) > 0.01f) {
+                        setValue_Anim(&d->pos, 1.0f, 250);
+                        setFlags_Anim(&d->pos, easeOut_AnimFlag, iFalse);
+                        startTimer_IndicatorWidget_(d);
+                    }
+                    else {
+                        setValue_Anim(&d->pos, 0, 0);
+                        stopTimer_IndicatorWidget_(d);
+                        refresh_Widget(d);
+                    }
+                }
+                else if (equal_Command(cmd, "cancelled")) {
+                    setValue_Anim(&d->pos, 0, 0);
+                    stopTimer_IndicatorWidget_(d);
+                    refresh_Widget(d);
+                }
+            }
+        }
+    }
+    return iFalse;
+}
+
+iBeginDefineSubclass(IndicatorWidget, Widget)
+    .draw         = (iAny *) draw_IndicatorWidget_,
+    .processEvent = (iAny *) processEvent_IndicatorWidget_,
+iEndDefineSubclass(IndicatorWidget)
diff --git a/src/ui/indicatorwidget.h b/src/ui/indicatorwidget.h
new file mode 100644
index 00000000..a3d9af39
--- /dev/null
+++ b/src/ui/indicatorwidget.h
@@ -0,0 +1,28 @@
+/* Copyright 2020 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. */
+
+#pragma once
+
+#include "widget.h"
+
+iDeclareWidgetClass(IndicatorWidget)
+iDeclareObjectConstruction(IndicatorWidget)
diff --git a/src/ui/util.c b/src/ui/util.c
index 38124b22..27950c5e 100644
--- a/src/ui/util.c
+++ b/src/ui/util.c
@@ -135,54 +135,15 @@ iBool isFinished_Anim(const iAnim *d) {
 }
 
 void init_Anim(iAnim *d, float value) {
-    d->due = d->when = SDL_GetTicks(); // frameTime_Window(get_Window());
+    d->due = d->when = SDL_GetTicks();
     d->from = d->to = value;
     d->flags = 0;
 }
 
-void setValue_Anim(iAnim *d, float to, uint32_t span) {
-    if (fabsf(to - d->to) > 0.00001f) {
-        const uint32_t now = SDL_GetTicks();
-        d->from = value_Anim(d);
-        d->to   = to;
-        d->when = now;
-        d->due  = now + span;
-    }
-}
-
 iLocalDef float pos_Anim_(const iAnim *d, uint32_t now) {
     return (float) (now - d->when) / (float) (d->due - d->when);
 }
 
-void setValueEased_Anim(iAnim *d, float to, uint32_t span) {
-    if (fabsf(to - d->to) <= 0.00001f) {
-        d->to = to; /* Pretty much unchanged. */
-        return;
-    }
-    const uint32_t now = SDL_GetTicks();
-    if (isFinished_Anim(d)) {
-        d->from  = d->to;
-        d->when  = now;
-        d->flags = easeBoth_AnimFlag;
-    }
-    else {
-        d->from  = value_Anim(d);
-        d->when  = frameTime_Window(get_Window()); /* to match the timing of value_Anim */
-        d->flags = easeOut_AnimFlag;
-    }
-    d->to  = to;
-    d->due = now + span;
-}
-
-void setFlags_Anim(iAnim *d, int flags, iBool set) {
-    iChangeFlags(d->flags, flags, set);
-}
-
-void stop_Anim(iAnim *d) {
-    d->from = d->to = value_Anim(d);
-    d->when = d->due = SDL_GetTicks();
-}
-
 iLocalDef float easeIn_(float t) {
     return t * t;
 }
@@ -198,8 +159,7 @@ iLocalDef float easeBoth_(float t) {
     return 0.5f + easeOut_((t - 0.5f) * 2.0f) * 0.5f;
 }
 
-float value_Anim(const iAnim *d) {
-    const uint32_t now = frameTime_Window(get_Window());
+static float valueAt_Anim_(const iAnim *d, const uint32_t now) {
     if (now >= d->due) {
         return d->to;
     }
@@ -219,6 +179,52 @@ float value_Anim(const iAnim *d) {
     return d->from * (1.0f - t) + d->to * t;
 }
 
+void setValue_Anim(iAnim *d, float to, uint32_t span) {
+    if (span == 0) {
+        d->from = d->to = to;
+        d->when = d->due = SDL_GetTicks();
+    }
+    else if (fabsf(to - d->to) > 0.00001f) {
+        const uint32_t now = SDL_GetTicks();
+        d->from = valueAt_Anim_(d, now);
+        d->to   = to;
+        d->when = now;
+        d->due  = now + span;
+    }
+}
+
+void setValueEased_Anim(iAnim *d, float to, uint32_t span) {
+    if (fabsf(to - d->to) <= 0.00001f) {
+        d->to = to; /* Pretty much unchanged. */
+        return;
+    }
+    const uint32_t now = SDL_GetTicks();
+    if (isFinished_Anim(d)) {
+        d->from  = d->to;
+        d->flags = easeBoth_AnimFlag;
+    }
+    else {
+        d->from  = valueAt_Anim_(d, now);
+        d->flags = easeOut_AnimFlag;
+    }
+    d->to   = to;
+    d->when = now;
+    d->due  = now + span;
+}
+
+void setFlags_Anim(iAnim *d, int flags, iBool set) {
+    iChangeFlags(d->flags, flags, set);
+}
+
+void stop_Anim(iAnim *d) {
+    d->from = d->to = value_Anim(d);
+    d->when = d->due = SDL_GetTicks();
+}
+
+float value_Anim(const iAnim *d) {
+    return valueAt_Anim_(d, frameTime_Window(get_Window()));
+}
+
 /*-----------------------------------------------------------------------------------------------*/
 
 void init_Click(iClick *d, iAnyObject *widget, int button) {
diff --git a/src/ui/widget.h b/src/ui/widget.h
index f39612ed..a1a38f28 100644
--- a/src/ui/widget.h
+++ b/src/ui/widget.h
@@ -160,6 +160,13 @@ iLocalDef iObjectList *children_Widget(iAnyObject *d) {
     iAssert(isInstance_Object(d, &Class_Widget));
     return ((iWidget *) d)->children;
 }
+iLocalDef iWidget *parent_Widget(const iAnyObject *d) {
+    if (d) {
+        iAssert(isInstance_Object(d, &Class_Widget));
+        return ((iWidget *) d)->parent;
+    }
+    return NULL;
+}
 
 iBool   isVisible_Widget    (const iAnyObject *);
 iBool   isDisabled_Widget   (const iAnyObject *);
Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.17/cdiff/859fad2c6d5013ace7fcb749b591468dd0b65612
Status Code
Success (20)
Meta
text/gemini; charset=utf-8
Capsule Response Time
75.588565 milliseconds
Gemini-to-HTML Time
0.699531 milliseconds

This content has been proxied by September (ba2dc).