From 2d81addf78d6a8b0fb2f2959b04a385c4adffdf2 Mon Sep 17 00:00:00 2001

From: =?UTF-8?q?Jaakko=20Kera=CC=88nen?= jaakko.keranen@iki.fi

Date: Mon, 20 Sep 2021 11:37:23 +0300

Subject: [PATCH 1/1] Experimenting with independent popup windows

Toe dipping into multiple window support by allowing popup menu widgets to be displayed in independent windows.

This is not a 100% replacement for native menus, but it gets pretty close.


src/app.c | 186 +++++++++++++++++++----------

src/app.h | 10 +-

src/ios.m | 10 +-

src/macos.h | 3 +

src/macos.m | 17 ++-

src/ui/documentwidget.c | 2 +-

src/ui/inputwidget.c | 2 +-

src/ui/root.c | 10 --

src/ui/root.h | 2 +-

src/ui/text.c | 93 ++++++++-------

src/ui/text.h | 17 ++-

src/ui/text_simple.c | 16 +--

src/ui/util.c | 42 ++++++-

src/ui/widget.c | 38 ++++--

src/ui/widget.h | 4 +-

src/ui/window.c | 257 ++++++++++++++++++++++++++++++----------

src/ui/window.h | 34 ++++--

17 files changed, 511 insertions(+), 232 deletions(-)

diff --git a/src/app.c b/src/app.c

index 91b3a06d..c1c1da27 100644

--- a/src/app.c

+++ b/src/app.c

@@ -118,6 +118,7 @@ struct Impl_App {

 iVisited *   visited;

 iBookmarks * bookmarks;

 iMainWindow *window;

 iSortedArray tickers; /* per-frame callbacks, used for animations */

 uint32_t     lastTickerTime;

 uint32_t     elapsedSinceLastTicker;

@@ -801,6 +802,7 @@ static void init_App_(iApp *d, int argc, char **argv) {

         d->initialWindowRect.size.y = toInt_String(value_CommandLineArg(arg, 0));

     }

 }

 d->window = new_MainWindow(d->initialWindowRect);

 load_Visited(d->visited, dataDir_App_());

 load_Bookmarks(d->bookmarks, dataDir_App_());

@@ -853,6 +855,11 @@ static void init_App_(iApp *d, int argc, char **argv) {

}

static void deinit_App(iApp *d) {

#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)

 SDL_RemoveTimer(d->sleepTimer);

#endif

@@ -1086,6 +1093,15 @@ static iBool nextEvent_App_(iApp *d, enum iAppEventMode eventMode, SDL_Event *ev

 return SDL_PollEvent(event);

}

+static const iPtrArray *listWindows_App_(const iApp *d) {

+}

void processEvents_App(enum iAppEventMode eventMode) {

 iApp *d = &app_;

 iRoot *oldCurrentRoot = current_Root(); /* restored afterwards */

@@ -1125,17 +1141,17 @@ void processEvents_App(enum iAppEventMode eventMode) {

#if defined (iPlatformAppleMobile)

             updateNowPlayingInfo_iOS();

#endif

             savePrefs_App_(d);

             saveState_App_(d);

             break;

         case SDL_APP_TERMINATING:

             savePrefs_App_(d);

             saveState_App_(d);

             break;

         case SDL_DROPFILE: {

             if (!wasUsed) {

                 iBool newTab = iFalse;

                 if (elapsedSeconds_Time(&d->lastDropTime) < 0.1) {

@@ -1175,23 +1191,6 @@ void processEvents_App(enum iAppEventMode eventMode) {

             }

             d->isIdling = iFalse;

#endif

-// if (ev.user.data2 == d->window->roots[0]) {

-// arrange_Widget(d->window->roots[0]->widget);

-// }

-// else if (d->window->roots[1]) {

-// arrange_Widget(d->window->roots[1]->widget);

-// }

-// postRefresh_App();

             gotEvents = iTrue;

             /* Keyboard modifier mapping. */

             if (ev.type == SDL_KEYDOWN || ev.type == SDL_KEYUP) {

@@ -1268,10 +1267,22 @@ void processEvents_App(enum iAppEventMode eventMode) {

                 }

             }

#endif

             if (!wasUsed) {

                 wasUsed = processEvent_Keys(&ev);

             }

             if (!wasUsed) {

@@ -1289,24 +1300,32 @@ void processEvents_App(enum iAppEventMode eventMode) {

                 handleCommand_MacOS(command_UserEvent(&ev));

#endif

                 if (isMetricsChange_UserEvent(&ev)) {

                     }

                 }

                 if (!wasUsed) {

                     /* No widget handled the command, so we'll do it. */

                     handleCommand_App(ev.user.data1);

                 }

                 /* Allocated by postCommand_Apps(). */

                 free(ev.user.data1);

             }

             }

             break;

         }

@@ -1394,25 +1413,46 @@ static int run_App_(iApp *d) {

void refresh_App(void) {

 iApp *d = &app_;

#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)

 if (d->warmupFrames == 0 && d->isIdling) {

     return;

 }

#endif

 if (!exchange_Atomic(&d->pendingRefresh, iFalse)) {

     return;

 }

-// iTime draw;

-// initCurrent_Time(&draw);

-// printf("draw: %lld \u03bcs\n", (long long) (elapsedSeconds_Time(&draw) * 1000000));

-// fflush(stdout);

 if (d->warmupFrames > 0) {

     d->warmupFrames--;

 }

@@ -1485,12 +1525,6 @@ void postRefresh_App(void) {

 }

}

-void postImmediateRefresh_App(void) {

-}

void postCommand_Root(iRoot *d, const char *command) {

 iAssert(command);

 if (strlen(command) == 0) {

@@ -1546,7 +1580,7 @@ void postCommandf_App(const char *command, ...) {

}

void rootOrder_App(iRoot *roots[2]) {

 roots[0] = win->keyRoot;

 roots[1] = (roots[0] == win->roots[0] ? win->roots[1] : win->roots[0]);

}

@@ -1583,6 +1617,16 @@ void removeTicker_App(iTickerFunc ticker, iAny *context) {

 remove_SortedArray(&d->tickers, &(iTicker){ context, NULL, ticker });

}

+void addPopup_App(iWindow *popup) {

+}

+void removePopup_App(iWindow *popup) {

+}

iMimeHooks *mimeHooks_App(void) {

 return app_.mimehooks;

}

@@ -1836,8 +1880,10 @@ iDocumentWidget *newTab_App(const iDocumentWidget *duplicateOf, iBool switchToNe

static iBool handleIdentityCreationCommands_(iWidget *dlg, const char *cmd) {

 iApp *d = &app_;

 if (equal_Command(cmd, "ident.showmore")) {

         if (flags_Widget(i.object) & collapse_WidgetFlag) {

             setFlags_Widget(i.object, hidden_WidgetFlag, iFalse);

         }

@@ -1978,9 +2024,15 @@ const iString *searchQueryUrl_App(const iString *queryStringUnescaped) {

 return collectNewFormat_String("%s?%s", cstr_String(&d->prefs.searchUrl), cstr_String(escaped));

}

+static void resetFonts_App_(iApp *d) {

+}

iBool handleCommand_App(const char *cmd) {

 iApp *d = &app_;

 if (equal_Command(cmd, "config.error")) {

     makeSimpleMessage_Widget(uiTextCaution_ColorEscape "CONFIG ERROR",

                              format_CStr("Error in config file: %s\n"

@@ -2047,18 +2099,18 @@ iBool handleCommand_App(const char *cmd) {

     return iTrue;

 }

 else if (equal_Command(cmd, "font.reset")) {

     return iTrue;

 }

 else if (equal_Command(cmd, "font.user")) {

     const char *path = suffixPtr_Command(cmd, "path");

     if (cmp_String(&d->prefs.symbolFontPath, path)) {

         if (!isFrozen) {

         }

         setCStr_String(&d->prefs.symbolFontPath, path);

         loadUserFonts_Text();

         if (!isFrozen) {

             postCommand_App("font.changed");

             postCommand_App("window.unfreeze");

@@ -2068,10 +2120,10 @@ iBool handleCommand_App(const char *cmd) {

 }

 else if (equal_Command(cmd, "font.set")) {

     if (!isFrozen) {

     }

     d->prefs.font = arg_Command(cmd);

     if (!isFrozen) {

         postCommand_App("font.changed");

         postCommand_App("window.unfreeze");

@@ -2080,10 +2132,10 @@ iBool handleCommand_App(const char *cmd) {

 }

 else if (equal_Command(cmd, "headingfont.set")) {

     if (!isFrozen) {

     }

     d->prefs.headingFont = arg_Command(cmd);

     if (!isFrozen) {

         postCommand_App("font.changed");

         postCommand_App("window.unfreeze");

@@ -2092,10 +2144,10 @@ iBool handleCommand_App(const char *cmd) {

 }

 else if (equal_Command(cmd, "zoom.set")) {

     if (!isFrozen) {

     }

     d->prefs.zoomPercent = arg_Command(cmd);

     if (!isFrozen) {

         postCommand_App("font.changed");

         postCommand_App("window.unfreeze");

@@ -2104,14 +2156,14 @@ iBool handleCommand_App(const char *cmd) {

 }

 else if (equal_Command(cmd, "zoom.delta")) {

     if (!isFrozen) {

     }

     int delta = arg_Command(cmd);

     if (d->prefs.zoomPercent < 100 || (delta < 0 && d->prefs.zoomPercent == 100)) {

         delta /= 2;

     }

     d->prefs.zoomPercent = iClamp(d->prefs.zoomPercent + delta, 50, 200);

     if (!isFrozen) {

         postCommand_App("font.changed");

         postCommand_App("window.unfreeze");

@@ -2211,7 +2263,7 @@ iBool handleCommand_App(const char *cmd) {

          equal_Command(cmd, "prefs.mono.gopher.changed")) {

     const iBool isSet = (arg_Command(cmd) != 0);

     if (!isFrozen) {

     }

     if (startsWith_CStr(cmd, "prefs.mono.gemini")) {

         d->prefs.monospaceGemini = isSet;

@@ -2936,3 +2988,7 @@ iStringSet *listOpenURLs_App(void) {

 iRelease(docs);

 return set;

}

+iMainWindow *mainWindow_App(void) {

+}

diff --git a/src/app.h b/src/app.h

index 08589000..8966e8c7 100644

--- a/src/app.h

+++ b/src/app.h

@@ -22,8 +22,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#pragma once

-/* Application core: event loop, base event processing, audio synth. */

#include <the_Foundation/objectlist.h>

#include <the_Foundation/string.h>

#include <the_Foundation/stringset.h>

@@ -35,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

iDeclareType(Bookmarks)

iDeclareType(DocumentWidget)

iDeclareType(GmCerts)

+iDeclareType(MainWindow)

iDeclareType(MimeHooks)

iDeclareType(Periodic)

iDeclareType(Root)

@@ -61,14 +60,12 @@ enum iAppEventMode {

enum iUserEventCode {

 command_UserEventCode = 1,

 refresh_UserEventCode,

 asleep_UserEventCode,

 /* The start of a potential touch tap event is notified via a custom event because

    sending SDL_MOUSEBUTTONDOWN would be premature: we don't know how long the tap will

    take, it could turn into a tap-and-hold for example. */

 widgetTapBegins_UserEventCode,

 widgetTouchEnds_UserEventCode, /* finger lifted, but momentum may continue */

};

const iString *execPath_App (void);

@@ -119,8 +116,9 @@ iAny * findWidget_App (const char *id);

void addTicker_App (iTickerFunc ticker, iAny *context);

void addTickerRoot_App (iTickerFunc ticker, iRoot *root, iAny *context);

void removeTicker_App (iTickerFunc ticker, iAny *context);

+void addPopup_App (iWindow *popup);

+void removePopup_App (iWindow *popup);

void postRefresh_App (void);

-void postImmediateRefresh_App(void);

void postCommand_Root (iRoot *, const char *command);

void postCommandf_Root (iRoot *, const char *command, ...);

void postCommandf_App (const char *command, ...);

@@ -138,3 +136,5 @@ iDocumentWidget * document_Command (const char *cmd);

void openInDefaultBrowser_App (const iString *url);

void revealPath_App (const iString *path);

+iMainWindow *mainWindow_App(void);

diff --git a/src/ios.m b/src/ios.m

index 3fb0af48..b46fb8dc 100644

--- a/src/ios.m

+++ b/src/ios.m

@@ -247,14 +247,14 @@ didPickDocumentsAtURLs:(NSArray<NSURL *> *)urls {

 UIView *view         = [viewController_(get_Window()) view];

 CGRect keyboardFrame = [view convertRect:rawFrame fromView:nil];

// NSLog(@"keyboardFrame: %@", NSStringFromCGRect(keyboardFrame));

}

-(void)keyboardOffScreen:(NSNotification *)notification {

}

@end

diff --git a/src/macos.h b/src/macos.h

index 0d3f097a..20b95943 100644

--- a/src/macos.h

+++ b/src/macos.h

@@ -24,6 +24,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include "ui/util.h"

+iDeclareType(Window)

/* Platform-specific functionality for macOS */

iBool shouldDefaultToMetalRenderer_MacOS (void);

@@ -31,6 +33,7 @@ iBool shouldDefaultToMetalRenderer_MacOS (void);

void enableMomentumScroll_MacOS (void);

void registerURLHandler_MacOS (void);

void setupApplication_MacOS (void);

+void hideTitleBar_MacOS (iWindow *window);

void insertMenuItems_MacOS (const char *menuLabel, int atIndex, const iMenuItem *items, size_t count);

void removeMenu_MacOS (int atIndex);

void enableMenu_MacOS (const char *menuLabel, iBool enable);

diff --git a/src/macos.m b/src/macos.m

index d588fa4a..298db0f8 100644

--- a/src/macos.m

+++ b/src/macos.m

@@ -30,6 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include "ui/window.h"

#include <SDL_timer.h>

+#include <SDL_syswm.h>

#import <AppKit/AppKit.h>

@@ -51,6 +52,16 @@ static iInt2 macVer_(void) {

 return init_I2(10, 10);

}

+static NSWindow *nsWindow_(SDL_Window *window) {

+}

static NSString *currentSystemAppearance_(void) {

 /* This API does not exist on 10.13. */

 if ([NSApp respondsToSelector:@selector(effectiveAppearance)]) {

@@ -370,6 +381,11 @@ void setupApplication_MacOS(void) {

 windowCloseItem.action = @selector(closeTab);

}

+void hideTitleBar_MacOS(iWindow *window) {

+}

void enableMenu_MacOS(const char *menuLabel, iBool enable) {

 menuLabel = translateCStr_Lang(menuLabel);

 NSApplication *app = [NSApplication sharedApplication];

@@ -377,7 +393,6 @@ void enableMenu_MacOS(const char *menuLabel, iBool enable) {

 NSString *label = [NSString stringWithUTF8String:menuLabel];

 NSMenuItem *menuItem = [appMenu itemAtIndex:[appMenu indexOfItemWithTitle:label]];

 [menuItem setEnabled:enable];

}

void enableMenuItem_MacOS(const char *menuItemCommand, iBool enable) {

diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c

index ed9e41d6..6f9824de 100644

--- a/src/ui/documentwidget.c

+++ b/src/ui/documentwidget.c

@@ -750,7 +750,7 @@ static uint32_t mediaUpdateInterval_DocumentWidget_(const iDocumentWidget *d) {

 if (document_App() != d) {

     return 0;

 }

     return 0;

 }

 static const uint32_t invalidInterval_ = ~0u;

diff --git a/src/ui/inputwidget.c b/src/ui/inputwidget.c

index a561d5bd..f02bf408 100644

--- a/src/ui/inputwidget.c

+++ b/src/ui/inputwidget.c

@@ -2352,7 +2352,7 @@ static void draw_InputWidget_(const iInputWidget *d) {

 }

 /* Draw the insertion point. */

 if (isFocused && d->cursorVis && contains_Range(&visLines, d->cursor.y) &&

     iInt2    curSize;

     iRangecc cursorChar    = iNullRange;

     int      visWrapsAbove = 0;

diff --git a/src/ui/root.c b/src/ui/root.c

index 52a08eca..9e290b05 100644

--- a/src/ui/root.c

+++ b/src/ui/root.c

@@ -298,16 +298,6 @@ void destroyPending_Root(iRoot *d) {

 setCurrent_Root(oldRoot);

}

-void postArrange_Root(iRoot *d) {

-}

iPtrArray *onTop_Root(iRoot *d) {

 if (!d->onTop) {

     d->onTop = new_PtrArray();

diff --git a/src/ui/root.h b/src/ui/root.h

index 740e97c9..04dd5e16 100644

--- a/src/ui/root.h

+++ b/src/ui/root.h

@@ -9,6 +9,7 @@ iDeclareType(Root)

struct Impl_Root {

 iWidget *  widget;

 iPtrArray *onTop; /* order is important; last one is topmost */

 iPtrSet *  pendingDestruction;

 iBool      pendingArrange;

@@ -29,7 +30,6 @@ iAnyObject *findWidget_Root (const char id); / under curre

iPtrArray * onTop_Root (iRoot *);

void destroyPending_Root (iRoot *);

-void postArrange_Root (iRoot *);

void updateMetrics_Root (iRoot *);

void updatePadding_Root (iRoot ); / TODO: is part of metrics? */

diff --git a/src/ui/text.c b/src/ui/text.c

index f7fff4bc..bf71b0e9 100644

--- a/src/ui/text.c

+++ b/src/ui/text.c

@@ -290,7 +290,9 @@ struct Impl_Text {

 iRegExp *      ansiEscape;

};

-static iText text_;

+iDefineTypeConstructionArgs(Text, (SDL_Renderer *render), render)

+static iText *activeText_;

static iBlock *userFont_;

static void initFonts_Text_(iText *d) {

@@ -501,8 +503,7 @@ void loadUserFonts_Text(void) {

 }

}

-void init_Text(SDL_Renderer *render) {

+void init_Text(iText *d, SDL_Renderer *render) {

 loadUserFonts_Text();

 d->contentFont     = nunito_TextFont;

 d->headingFont     = nunito_TextFont;

@@ -521,8 +522,7 @@ void init_Text(SDL_Renderer *render) {

 initFonts_Text_(d);

}

-void deinit_Text(void) {

+void deinit_Text(iText *d) {

 SDL_FreePalette(d->grayscale);

 deinitFonts_Text_(d);

 deinitCache_Text_(d);

@@ -530,30 +530,34 @@ void deinit_Text(void) {

 iRelease(d->ansiEscape);

}

+void setCurrent_Text(iText *d) {

+}

void setOpacity_Text(float opacity) {

}

-void setContentFont_Text(enum iTextFont font) {

+void setContentFont_Text(iText *d, enum iTextFont font) {

 }

}

-void setHeadingFont_Text(enum iTextFont font) {

+void setHeadingFont_Text(iText *d, enum iTextFont font) {

 }

}

-void setContentFontSize_Text(float fontSizeFactor) {

+void setContentFontSize_Text(iText *d, float fontSizeFactor) {

 fontSizeFactor *= contentScale_Text_;

 iAssert(fontSizeFactor > 0);

 }

}

@@ -565,8 +569,7 @@ static void resetCache_Text_(iText *d) {

 initCache_Text_(d);

}

-void resetFonts_Text(void) {

+void resetFonts_Text(iText *d) {

 deinitFonts_Text_(d);

 deinitCache_Text_(d);

 initCache_Text_(d);

@@ -574,7 +577,7 @@ void resetFonts_Text(void) {

}

iLocalDef iFont *font_Text_(enum iFontId id) {

}

static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, float xShift) {

@@ -584,7 +587,7 @@ static SDL_Surface *rasterizeGlyph_Font_(const iFont *d, uint32_t glyphIndex, fl

 SDL_Surface *surface8 =

     SDL_CreateRGBSurfaceWithFormatFrom(bmp, w, h, 8, w, SDL_PIXELFORMAT_INDEX8);

 SDL_SetSurfaceBlendMode(surface8, SDL_BLENDMODE_NONE);

#if LAGRANGE_RASTER_DEPTH != 8

 /* Convert to the cache format. */

 SDL_Surface *surf = SDL_ConvertSurfaceFormat(surface8, LAGRANGE_RASTER_FORMAT, 0);

@@ -631,7 +634,7 @@ static void allocate_Font_(iFont *d, iGlyph *glyph, int hoff) {

     &d->font, index_Glyph_(glyph), d->xScale, d->yScale, hoff * 0.5f, 0.0f, &x0, &y0, &x1, &y1);

 glRect->size = init_I2(x1 - x0, y1 - y0);

 /* Determine placement in the glyph cache texture, advancing in rows. */

 glyph->d[hoff] = init_I2(x0, y0);

 glyph->d[hoff].y += d->vertOffset;

 if (hoff == 0) { /* hoff==1 uses same metrics as `glyph` */

@@ -737,11 +740,11 @@ static iGlyph *glyphByIndex_Font_(iFont *d, uint32_t glyphIndex) {

 }

 else {

     /* If the cache is running out of space, clear it and we'll recache what's needed currently. */

#if !defined (NDEBUG)

         printf("[Text] glyph cache is full, clearing!\n"); fflush(stdout);

#endif

     }

     glyph       = new_Glyph(glyphIndex);

     glyph->font = d;

@@ -858,7 +861,7 @@ static void finishRun_AttributedText_(iAttributedText *d, iAttributedRun *run, i

}

static enum iFontId fontId_Text_(const iFont *font) {

}

static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iChar overrideChar) {

@@ -949,7 +952,7 @@ static void prepare_AttributedText_(iAttributedText *d, int overrideBaseDir, iCh

         /* Do a regexp match in the source text. */

         iRegExpMatch m;

         init_RegExpMatch(&m);

             finishRun_AttributedText_(d, &run, pos - 1);

             run.fgColor = ansiForeground_Color(capturedRange_RegExpMatch(&m, 1),

                                                tmParagraph_ColorId);

@@ -1082,9 +1085,9 @@ static void cacheGlyphs_Font_(iFont *d, const iArray *glyphIndices) {

 while (index < size_Array(glyphIndices)) {

     for (; index < size_Array(glyphIndices); index++) {

         const uint32_t glyphIndex = constValue_Array(glyphIndices, index, uint32_t);

         iGlyph *glyph = glyphByIndex_Font_(d, glyphIndex);

             /* The cache was reset due to running out of space. We need to restart from

                the beginning! */

             bufX = 0;

@@ -1103,7 +1106,7 @@ static void cacheGlyphs_Font_(iFont *d, const iArray *glyphIndices) {

                             LAGRANGE_RASTER_DEPTH,

                             LAGRANGE_RASTER_FORMAT);

                 SDL_SetSurfaceBlendMode(buf, SDL_BLENDMODE_NONE);

             }

             SDL_Surface *surfaces[2] = {

                 !isRasterized_Glyph_(glyph, 0) ?

@@ -1147,19 +1150,19 @@ static void cacheGlyphs_Font_(iFont *d, const iArray *glyphIndices) {

     }

     /* Finished or the buffer is full, copy the glyphs to the cache texture. */

     if (!isEmpty_Array(rasters)) {

         SDL_SetTextureBlendMode(bufTex, SDL_BLENDMODE_NONE);

         if (!isTargetChanged) {

             isTargetChanged = iTrue;

         }

// printf("copying %zu rasters from %p\n", size_Array(rasters), bufTex); fflush(stdout);

         iConstForEach(Array, i, rasters) {

             const iRasterGlyph *rg = i.value;

// iAssert(isEqual_I2(rg->rect.size, rg->glyph->rect[rg->hoff].size));

             const iRect *glRect = &rg->glyph->rect[rg->hoff];

                            bufTex,

                            (const SDL_Rect *) &rg->rect,

                            (const SDL_Rect *) glRect);

@@ -1179,7 +1182,7 @@ static void cacheGlyphs_Font_(iFont *d, const iArray *glyphIndices) {

     SDL_FreeSurface(buf);

 }

 if (isTargetChanged) {

 }

}

@@ -1706,9 +1709,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {

                 }

                 if (~mode & permanentColorFlag_RunMode) {

                     const iColor clr = run->fgColor;

                     if (args->mode & fillBackground_RunMode) {

                     }

                 }

                 SDL_Rect src;

@@ -1719,9 +1722,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {

                     /* Alpha blending looks much better if the RGB components don't change in

                        the partially transparent pixels. */

                     /* TODO: Backgrounds of all glyphs should be cleared before drawing anything else. */

                 }

#if 0

                 /* Show spaces and direction. */

                 if (logicalText[logPos] == 0x20) {

@@ -1863,7 +1866,7 @@ iTextMetrics measureN_Text(int fontId, const char *text, size_t n) {

}

static void drawBoundedN_Text_(int fontId, iInt2 pos, int xposBound, int color, iRangecc text, size_t maxLen) {

 iFont *      font = font_Text_(fontId);

 const iColor clr  = get_Color(color & mask_ColorId);

 SDL_SetTextureColorMod(d->cache, clr.r, clr.g, clr.b);

@@ -2057,7 +2060,7 @@ iTextMetrics draw_WrapText(iWrapText *d, int fontId, iInt2 pos, int color) {

}

SDL_Texture *glyphCache_Text(void) {

}

static void freeBitmap_(void *ptr) {

@@ -2170,7 +2173,7 @@ iString *renderBlockChars_Text(const iBlock *fontData, int height, enum iTextBlo

iDefineTypeConstructionArgs(TextBuf, (iWrapText *wrapText, int font, int color), wrapText, font, color)

void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {

 d->size = measure_WrapText(wrapText, font).bounds.size;

 SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");

 if (d->size.x * d->size.y) {

@@ -2191,9 +2194,9 @@ void init_TextBuf(iTextBuf *d, iWrapText *wrapText, int font, int color) {

     SDL_SetRenderDrawBlendMode(render, SDL_BLENDMODE_NONE);

     SDL_SetRenderDrawColor(render, 255, 255, 255, 0);

     SDL_RenderClear(render);

     draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId);

     SDL_SetRenderTarget(render, oldTarget);

     origin_Paint = oldOrigin;

     SDL_SetTextureBlendMode(d->texture, SDL_BLENDMODE_BLEND);

@@ -2212,7 +2215,7 @@ void draw_TextBuf(const iTextBuf *d, iInt2 pos, int color) {

 addv_I2(&pos, origin_Paint);

 const iColor clr = get_Color(color);

 SDL_SetTextureColorMod(d->texture, clr.r, clr.g, clr.b);

                d->texture,

                &(SDL_Rect){ 0, 0, d->size.x, d->size.y },

                &(SDL_Rect){ pos.x, pos.y, d->size.x, d->size.y });

diff --git a/src/ui/text.h b/src/ui/text.h

index ac6cc1c1..1da43818 100644

--- a/src/ui/text.h

+++ b/src/ui/text.h

@@ -139,15 +139,20 @@ enum iTextFont {

extern int gap_Text; /* affected by content font size */

-void init_Text (SDL_Renderer *);

-void deinit_Text (void);

+iDeclareType(Text)

+iDeclareTypeConstructionArgs(Text, SDL_Renderer *)

+void init_Text (iText *, SDL_Renderer *);

+void deinit_Text (iText *);

+void setCurrent_Text (iText *);

void loadUserFonts_Text (void); /* based on Prefs */

-void setContentFont_Text (enum iTextFont font);

-void setHeadingFont_Text (enum iTextFont font);

-void setContentFontSize_Text (float fontSizeFactor); /* affects all except default* fonts */

-void resetFonts_Text (void);

+void setContentFont_Text (iText *, enum iTextFont font);

+void setHeadingFont_Text (iText *, enum iTextFont font);

+void setContentFontSize_Text (iText , float fontSizeFactor); / affects all except default* fonts */

+void resetFonts_Text (iText *);

int lineHeight_Text (int fontId);

iRect visualBounds_Text (int fontId, iRangecc text);

diff --git a/src/ui/text_simple.c b/src/ui/text_simple.c

index bf33b4be..8b1de64a 100644

--- a/src/ui/text_simple.c

+++ b/src/ui/text_simple.c

@@ -92,7 +92,7 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {

 }

 if (args->mode & fillBackground_RunMode) {

     const iColor initial = get_Color(args->color);

 }

 /* Text rendering is not very straightforward! Let's dive in... */

 iChar       prevCh = 0;

@@ -114,14 +114,14 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {

         chPos++;

         iRegExpMatch m;

         init_RegExpMatch(&m);

             if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) {

                 /* Change the color. */

                 const iColor clr =

                     ansiForeground_Color(capturedRange_RegExpMatch(&m, 1), tmParagraph_ColorId);

                 if (args->mode & fillBackground_RunMode) {

                 }

             }

             chPos = end_RegExpMatch(&m);

@@ -205,9 +205,9 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {

             }

             if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) {

                 const iColor clr = get_Color(colorNum);

                 if (args->mode & fillBackground_RunMode) {

                 }

             }

             prevCh = 0;

@@ -311,9 +311,9 @@ static iRect runSimple_Font_(iFont *d, const iRunArgs *args) {

         if (args->mode & fillBackground_RunMode) {

             /* Alpha blending looks much better if the RGB components don't change in

                the partially transparent pixels. */

         }

     }

     xpos += advance;

     if (!isSpace_Char(ch)) {

diff --git a/src/ui/util.c b/src/ui/util.c

index 721aed2d..38977b96 100644

--- a/src/ui/util.c

+++ b/src/ui/util.c

@@ -613,6 +613,8 @@ iBool isAction_Widget(const iWidget *d) {

/-----------------------------------------------------------------------------------------------/

static iBool isCommandIgnoredByMenus_(const char *cmd) {

 /* TODO: Perhaps a common way of indicating which commands are notifications and should not

    be reacted to by menus? */

 return equal_Command(cmd, "media.updated") ||

@@ -810,6 +812,10 @@ static void updateMenuItemFonts_Widget_(iWidget *d) {

 }

}

+iLocalDef iBool isUsingMenuPopupWindows_(void) {

+}

void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) {

 const iRect rootRect        = rect_Root(d->root);

 const iInt2 rootSize        = rootRect.size;

@@ -822,6 +828,26 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) {

 processEvents_App(postedEventsOnly_AppEventMode);

 setFlags_Widget(d, hidden_WidgetFlag, iFalse);

 setFlags_Widget(d, commandOnMouseMiss_WidgetFlag, iTrue);

 raise_Widget(d);

 setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iFalse);

 if (isPortraitPhone) {

@@ -836,7 +862,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) {

 arrange_Widget(d);

 if (isPortraitPhone) {

     if (isSlidePanel) {

     }

     else {

         d->rect.pos = init_I2(0, rootSize.y);

@@ -856,7 +882,7 @@ void openMenuFlags_Widget(iWidget *d, iInt2 windowCoord, iBool postCommands) {

     float l, t, r, b;

     safeAreaInsets_iOS(&l, &t, &r, &b);

     topExcess    += t;

     leftExcess   += l;

     rightExcess  += r;

 }

@@ -884,6 +910,18 @@ void closeMenu_Widget(iWidget *d) {

 if (d == NULL || flags_Widget(d) & hidden_WidgetFlag) {

     return; /* Already closed. */

 }

 setFlags_Widget(d, hidden_WidgetFlag, iTrue);

 setFlags_Widget(findChild_Widget(d, "menu.cancel"), disabled_WidgetFlag, iTrue);

 postRefresh_App();

diff --git a/src/ui/widget.c b/src/ui/widget.c

index 23c19315..7b33a752 100644

--- a/src/ui/widget.c

+++ b/src/ui/widget.c

@@ -271,6 +271,10 @@ iWidget *root_Widget(const iWidget *d) {

 return d ? d->root->widget : NULL;

}

+iWindow *window_Widget(const iAnyObject *d) {

+}

void showCollapsed_Widget(iWidget *d, iBool show) {

 const iBool isVisible = !(d->flags & hidden_WidgetFlag);

 if ((isVisible && !show) || (!isVisible && show)) {

@@ -979,11 +983,10 @@ void unhover_Widget(void) {

}

iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {

 if (!d->parent) {

         /* Root dispatches keyboard events directly to the focused widget. */

             return iTrue;

         }

     }

@@ -1012,7 +1015,8 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {

     }

 }

 else if (ev->type == SDL_MOUSEMOTION &&

          flags_Widget(d) & hover_WidgetFlag && ~flags_Widget(d) & hidden_WidgetFlag &&

          ~flags_Widget(d) & disabled_WidgetFlag) {

     if (contains_Widget(d, init_I2(ev->motion.x, ev->motion.y))) {

@@ -1031,11 +1035,11 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {

     iReverseForEach(ObjectList, i, d->children) {

         iWidget *child = as_Widget(i.object);

         //iAssert(child->root == d->root);

             continue; /* Already dispatched. */

         }

         if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) {

             continue;

         }

         if (dispatchEvent_Widget(child, ev)) {

@@ -1050,7 +1054,7 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {

#endif

#if 0

             if (ev->type == SDL_MOUSEMOTION) {

                        child, class_Widget(child)->name,

                        cstr_String(id_Widget(child)));

                 fflush(stdout);

@@ -1246,7 +1250,7 @@ iBool processEvent_Widget(iWidget *d, const SDL_Event *ev) {

                            ev->button.x,

                            ev->button.y);

     }

     return iTrue;

 }

 return iFalse;

@@ -1270,6 +1274,7 @@ iLocalDef iBool isDrawn_Widget_(const iWidget *d) {

void drawLayerEffects_Widget(const iWidget *d) {

 /* Layered effects are not buffered, so they are drawn here separately. */

 iAssert(isDrawn_Widget_(d));

 iBool shadowBorder   = (d->flags & keepOnTop_WidgetFlag && ~d->flags & mouseModal_WidgetFlag) != 0;

 iBool fadeBackground = (d->bgColor >= 0 || d->frameColor >= 0) && d->flags & mouseModal_WidgetFlag;

 if (deviceType_App() == phone_AppDeviceType) {

@@ -1539,6 +1544,7 @@ static void endBufferDraw_Widget_(const iWidget *d) {

}

void draw_Widget(const iWidget *d) {

 if (!isDrawn_Widget_(d)) {

     if (d->drawBuf) {

// printf("[%p] drawBuffer released\n", d);

@@ -1820,7 +1826,17 @@ iBool equalWidget_Command(const char *cmd, const iWidget *widget, const char *ch

 if (equal_Command(cmd, checkCommand)) {

     const iWidget *src = pointer_Command(cmd);

     iAssert(!src || strstr(cmd, " ptr:"));

+// if (src && type_Window(window_Widget(src)) == popup_WindowType) {

+// /* Special case: command was emitted from a popup widget. The popup root widget actually

+// belongs to someone else. */

+// iWidget *realParent = userData_Object(src->root->widget);

+// iAssert(realParent);

+// iAssert(isInstance_Object(realParent, &Class_Widget));

+// return realParent == widget || hasParent_Widget(realParent, widget);

+// }

 }

 return iFalse;

}

@@ -1962,6 +1978,10 @@ void postCommand_Widget(const iAnyObject *d, const char *cmd, ...) {

 }

 if (!isGlobal) {

     iAssert(isInstance_Object(d, &Class_Widget));

     appendFormat_String(&str, " ptr:%p", d);

 }

 postCommandString_Root(((const iWidget *) d)->root, &str);

diff --git a/src/ui/widget.h b/src/ui/widget.h

index 7491cb79..0eab69c1 100644

--- a/src/ui/widget.h

+++ b/src/ui/widget.h

@@ -34,7 +34,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include <the_Foundation/string.h>

#include <SDL_events.h>

-iDeclareType(Root) /* each widget is associated with a Root */

+iDeclareType(Root) /* each widget is associated with a Root */

+iDeclareType(Window) /* each Root is inside a Window */

#define iDeclareWidgetClass(className) \

 iDeclareType(className); \

@@ -185,6 +186,7 @@ void releaseChildren_Widget (iWidget *);

 - inner:  0,0 is at the top left corner of the widget */

iWidget * root_Widget (const iWidget *);

+iWindow * window_Widget (const iAnyObject *);

const iString * id_Widget (const iWidget *);

int64_t flags_Widget (const iWidget *);

iRect bounds_Widget (const iWidget ); / outer bounds */

diff --git a/src/ui/window.c b/src/ui/window.c

index 92125d81..e9a34ace 100644

--- a/src/ui/window.c

+++ b/src/ui/window.c

@@ -57,7 +57,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include "stb_image.h"

#include "stb_image_resize.h"

-static iWindow *theWindow_ = NULL;

+static iWindow * theWindow_;

+static iMainWindow *theMainWindow_;

#if defined (iPlatformApple) || defined (iPlatformLinux) || defined (iPlatformOther)

static float initialUiScale_ = 1.0f;

@@ -67,6 +68,9 @@ static float initialUiScale_ = 1.1f;

static iBool isOpenGLRenderer_;

+iDefineTypeConstructionArgs(Window,

iDefineTypeConstructionArgs(MainWindow, (iRect rect), rect)

/* TODO: Define menus per platform. */

@@ -205,6 +209,7 @@ static void setupUserInterface_MainWindow(iMainWindow *d) {

#endif

 /* One root is created by default. */

 d->base.roots[0] = new_Root();

 setCurrent_Root(d->base.roots[0]);

 createUserInterface_Root(d->base.roots[0]);

 setCurrent_Root(NULL);

@@ -409,7 +414,6 @@ void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags)

 d->mouseGrab     = NULL;

 d->focus         = NULL;

 d->pendingCursor = NULL;

 d->isExposed     = iFalse;

 d->isMinimized   = iFalse;

 d->isInvalidated = iFalse; /* set when posting event, to avoid repeated events */

@@ -441,9 +445,27 @@ void init_Window(iWindow *d, enum iWindowType type, iRect rect, uint32_t flags)

 d->uiScale      = initialUiScale_;

 /* TODO: Ratios, scales, and metrics must be window-specific, not global. */

 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);

+}

+static void deinitRoots_Window_(iWindow *d) {

}

void deinit_Window(iWindow *d) {

 SDL_DestroyRenderer(d->render);

 SDL_DestroyWindow(d->win);

 iForIndices(i, d->cursors) {

@@ -455,6 +477,7 @@ void deinit_Window(iWindow *d) {

void init_MainWindow(iMainWindow *d, iRect rect) {

 theWindow_ = &d->base;

 uint32_t flags = 0;

#if defined (iPlatformAppleDesktop)

 SDL_SetHint(SDL_HINT_RENDER_DRIVER, shouldDefaultToMetalRenderer_MacOS() ? "metal" : "opengl");

@@ -465,13 +488,15 @@ void init_MainWindow(iMainWindow *d, iRect rect) {

#endif

 SDL_SetHint(SDL_HINT_RENDER_VSYNC, "1");

 init_Window(&d->base, main_WindowType, rect, flags);

 d->place.lastNotifiedSize = zero_I2();

#if defined(iPlatformMobile)

 const iInt2 minSize = zero_I2(); /* windows aren't independently resizable */

#else

@@ -510,9 +535,9 @@ void init_MainWindow(iMainWindow *d, iRect rect) {

 }

#endif

#if defined (iPlatformAppleMobile)

#endif

 SDL_GetRendererOutputSize(d->base.render, &d->base.size.x, &d->base.size.y);    

 setupUserInterface_MainWindow(d);

 postCommand_App("~bindings.changed"); /* update from bindings */

@@ -538,24 +563,15 @@ void init_MainWindow(iMainWindow *d, iRect rect) {

#endif

}

-static void deinitRoots_Window_(iWindow *d) {

-}

void deinit_MainWindow(iMainWindow *d) {

 deinitRoots_Window_(as_Window(d));

 if (theWindow_ == as_Window(d)) {

     theWindow_ = NULL;

 }

 delete_String(d->pendingSplitUrl);

 deinit_Window(&d->base);

}

@@ -592,7 +608,7 @@ iRoot *otherRoot_Window(const iWindow *d, iRoot *root) {

static void invalidate_MainWindow_(iMainWindow *d, iBool forced) {

 if (d && (!d->base.isInvalidated || forced)) {

     d->base.isInvalidated = iTrue;

     postCommand_App("theme.changed auto:1"); /* forces UI invalidation */

 }

}

@@ -607,7 +623,7 @@ void invalidate_Window(iAnyWindow *d) {

}

static iBool isNormalPlacement_MainWindow_(const iMainWindow *d) {

#if defined (iPlatformApple)

 /* Maximized mode is not special on macOS. */

 if (snap_MainWindow(d) == maximized_WindowSnap) {

@@ -655,7 +671,7 @@ static iBool unsnap_MainWindow_(iMainWindow *d, const iInt2 *newPos) {

static void notifyMetricsChange_Window_(const iWindow *d) {

 /* Dynamic UI metrics change. Widgets need to update themselves. */

 setScale_Metrics(d->pixelRatio * d->displayScale * d->uiScale);

 postCommand_App("metrics.changed");

}

@@ -676,6 +692,41 @@ static void checkPixelRatioChange_Window_(iWindow *d) {

 }

}

+static iBool handleWindowEvent_Window_(iWindow *d, const SDL_WindowEvent *ev) {

+// SDL_SetWindowInputFocus(mainWindow_App()->base.win);

+}

static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent *ev) {

 switch (ev->event) {

#if defined(iPlatformDesktop)

@@ -795,6 +846,7 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent

         return iTrue;

     case SDL_WINDOWEVENT_ENTER:

         d->base.isMouseInside = iTrue;

         postCommand_App("window.mouse.entered");

         return iTrue;

     case SDL_WINDOWEVENT_FOCUS_GAINED:

@@ -802,16 +854,16 @@ static iBool handleWindowEvent_MainWindow_(iMainWindow *d, const SDL_WindowEvent

         setCapsLockDown_Keys(iFalse);

         postCommand_App("window.focus.gained");

         d->base.isExposed = iTrue;

-#if !defined(iPlatformDesktop)

+#if !defined (iPlatformDesktop)

         /* Returned to foreground, may have lost buffered content. */

         postCommand_App("window.unfreeze");

#endif

         return iFalse;

     case SDL_WINDOWEVENT_FOCUS_LOST:

         postCommand_App("window.focus.lost");

-#if !defined(iPlatformDesktop)

+#if !defined (iPlatformDesktop)

#endif

         return iFalse;

     case SDL_WINDOWEVENT_TAKE_FOCUS:

@@ -831,8 +883,8 @@ static void applyCursor_Window_(iWindow *d) {

 }

}

-iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {

+iBool processEvent_Window(iWindow *d, const SDL_Event *ev) {

 switch (ev->type) {

#if defined (LAGRANGE_ENABLE_CUSTOM_FRAME)

     case SDL_SYSWMEVENT: {

@@ -845,19 +897,26 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {

     }

#endif

     case SDL_WINDOWEVENT: {

     }

     case SDL_RENDER_TARGETS_RESET:

     case SDL_RENDER_DEVICE_RESET: {

         break;

     }

     default: {

         SDL_Event event = *ev;

         if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) {

             }

             postRefresh_App();

             postCommand_App("media.player.update"); /* in case a player needs updating */

@@ -866,35 +925,35 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {

         if (processEvent_Touch(&event)) {

             return iTrue;

         }

             /* Suspiciously close to when input focus was received. For example under openbox,

                closing xterm with Ctrl+D will cause the keydown event to "spill" over to us.

                As a workaround, ignore these events. */

             return iTrue; /* won't go to bindings, either */

         }

             return iTrue;

         }

         /* Map mouse pointer coordinate to our coordinate system. */

         if (event.type == SDL_MOUSEMOTION) {

             event.motion.x = pos.x;

             event.motion.y = pos.y;

         }

         else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {

             event.button.x = pos.x;

             event.button.y = pos.y;

             if (event.type == SDL_MOUSEBUTTONDOWN) {

                 /* Button clicks will change keyroot. */

                     const iInt2 click = init_I2(event.button.x, event.button.y);

                             break;

                         }

                     }

@@ -909,13 +968,13 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {

             event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {

             if (mouseGrab_Widget()) {

                 iWidget *grabbed = mouseGrab_Widget();

                 wasUsed = dispatchEvent_Widget(grabbed, &event);

             }

         }

         /* Dispatch the event to the tree of widgets. */

         if (!wasUsed) {

         }

         if (!wasUsed) {

             /* As a special case, clicking the middle mouse button can be used for pasting

@@ -928,35 +987,35 @@ iBool processEvent_MainWindow(iMainWindow *d, const SDL_Event *ev) {

                 paste.key.keysym.mod = KMOD_PRIMARY;

                 paste.key.state      = SDL_PRESSED;

                 paste.key.timestamp  = SDL_GetTicks();

             }

             if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {

                     wasUsed = iTrue;

                 }

             }

         }

         if (isMetricsChange_UserEvent(&event)) {

             }

         }

#if defined (iHaveNativeMenus)

             /* Retranslate the menus. */

             removeMacMenus_();

             insertMacMenus_();

#endif

                 }

             }

         }

         if (event.type == SDL_MOUSEMOTION) {

         }

         return wasUsed;

     }

@@ -1003,6 +1062,9 @@ iBool dispatchEvent_Window(iWindow *d, const SDL_Event *ev) {

                                                          coord_MouseWheelEvent(&ev->wheel))) {

             continue; /* Only process the event in the relevant split. */

         }

         setCurrent_Root(root);

         const iBool wasUsed = dispatchEvent_Widget(root->widget, ev);

         if (wasUsed) {

@@ -1044,11 +1106,40 @@ iBool postContextClick_Window(iWindow *d, const SDL_MouseButtonEvent *ev) {

 return iFalse;

}

+void draw_Window(iWindow *d) {

+#if !defined (NDEBUG)

+#endif

+// drawRectThickness_Paint(&p, (iRect){ zero_I2(), sub_I2(d->size, one_I2()) }, gap_UI / 4, uiSeparator_ColorId);

+}

void draw_MainWindow(iMainWindow *d) {

 iWindow *w = as_Window(d);

     return;

 }

 /* Check if root needs resizing. */ {

     iInt2 renderSize;

     SDL_GetRendererOutputSize(w->render, &renderSize.x, &renderSize.y);

@@ -1180,7 +1271,7 @@ void setUiScale_Window(iWindow *d, float uiScale) {

 }

}

-void setFreezeDraw_Window(iWindow *d, iBool freezeDraw) {

+void setFreezeDraw_MainWindow(iMainWindow *d, iBool freezeDraw) {

 d->isDrawFrozen = freezeDraw;

}

@@ -1231,8 +1322,23 @@ iWindow *get_Window(void) {

 return theWindow_;

}

+void setCurrent_Window(iAnyWindow *d) {

+}

iMainWindow *get_MainWindow(void) {

}

iBool isOpenGLRenderer_Window(void) {

@@ -1272,7 +1378,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {

 iAssert(current_Root() == NULL);

 if (d->splitMode != splitMode) {

     int oldCount = numRoots_Window(w);

     if (oldCount == 2 && splitMode == 0) {

         /* Keep references to the tabs of the second root. */

         const iDocumentWidget *curPage = document_Root(w->keyRoot);

@@ -1311,6 +1417,7 @@ void setSplitMode_MainWindow(iMainWindow *d, int splitFlags) {

         }

         w->roots[newRootIndex] = new_Root();

         w->keyRoot             = w->roots[newRootIndex];

         setCurrent_Root(w->roots[newRootIndex]);

         createUserInterface_Root(w->roots[newRootIndex]);

         if (!isEmpty_String(d->pendingSplitUrl)) {

@@ -1471,3 +1578,25 @@ int snap_MainWindow(const iMainWindow *d) {

 }

 return d->place.snap;

}

+/----------------------------------------------------------------------------------------------/

+iWindow *newPopup_Window(iInt2 screenPos, iWidget *rootWidget) {

+#if defined (iPlatformAppleDesktop)

+#endif

+}

diff --git a/src/ui/window.h b/src/ui/window.h

index 73e92391..f1827931 100644

--- a/src/ui/window.h

+++ b/src/ui/window.h

@@ -29,8 +29,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */

#include <SDL_render.h>

#include <SDL_video.h>

+enum iWindowType {

+};

iDeclareType(MainWindow)

+iDeclareType(Text)

iDeclareType(Window)

+iDeclareTypeConstructionArgs(Window, enum iWindowType type, iRect rect, uint32_t flags)

iDeclareTypeConstructionArgs(MainWindow, iRect rect)

typedef iAny iAnyWindow;

@@ -71,15 +79,9 @@ enum iWindowSplit {

 noEvents_WindowSplit = iBit(11),

};

-enum iWindowType {

-};

struct Impl_Window {

 enum iWindowType type;

 SDL_Window *  win;

 iBool         isExposed;

 iBool         isMinimized;

 iBool         isMouseInside;

@@ -102,11 +104,13 @@ struct Impl_Window {

 iRoot *       roots[2];     /* root widget and UI state; second one is for split mode */

 iRoot *       keyRoot;      /* root that has the current keyboard input focus */

 SDL_Texture * borderShadow;

};

struct Impl_MainWindow {

 iWindow       base;

 iWindowPlacement place;

 int           splitMode;

 int           pendingSplitMode;

 iString *     pendingSplitUrl; /* URL to open in a newly opened split */

@@ -115,7 +119,10 @@ struct Impl_MainWindow {

};

iLocalDef enum iWindowType type_Window(const iAnyWindow *d) {

}

uint32_t id_Window (const iWindow *);

@@ -131,11 +138,11 @@ int numRoots_Window (const iWindow *);

iRoot * findRoot_Window (const iWindow *, const iWidget *widget);

iRoot * otherRoot_Window (const iWindow *, iRoot *root);

+iBool processEvent_Window (iWindow *, const SDL_Event *);

iBool dispatchEvent_Window (iWindow *, const SDL_Event *);

void invalidate_Window (iAnyWindow ); / discard all cached graphics */

void draw_Window (iWindow *);

void setUiScale_Window (iWindow *, float uiScale);

-void setFreezeDraw_Window (iWindow *, iBool freezeDraw);

void setCursor_Window (iWindow *, int cursor);

iBool setKeyRoot_Window (iWindow *, iRoot *root);

iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *);

@@ -143,6 +150,8 @@ iBool postContextClick_Window (iWindow *, const SDL_MouseButtonEvent *);

iWindow * get_Window (void);

iBool isOpenGLRenderer_Window (void);

+void setCurrent_Window (iAnyWindow *);

iLocalDef iBool isExposed_Window(const iWindow *d) {

 iAssert(d);

 return d->isExposed;

@@ -158,6 +167,10 @@ iLocalDef const iWindow *constAs_Window(const iAnyWindow *d) {

 return (const iWindow *) d;

}

+iLocalDef iText *text_Window(const iAnyWindow *d) {

+}

/----------------------------------------------------------------------------------------------/

iLocalDef iWindow *asWindow_MainWindow(iMainWindow *d) {

@@ -167,6 +180,7 @@ iLocalDef iWindow *asWindow_MainWindow(iMainWindow *d) {

void setTitle_MainWindow (iMainWindow *, const iString *title);

void setSnap_MainWindow (iMainWindow *, int snapMode);

+void setFreezeDraw_MainWindow (iMainWindow *, iBool freezeDraw);

void setKeyboardHeight_MainWindow (iMainWindow *, int height);

void setSplitMode_MainWindow (iMainWindow *, int splitMode);

void checkPendingSplit_MainWindow (iMainWindow *);

@@ -196,3 +210,7 @@ iLocalDef const iMainWindow *constAs_MainWindow(const iAnyWindow *d) {

 iAssert(type_Window(d) == main_WindowType);

 return (const iMainWindow *) d;

}

+/----------------------------------------------------------------------------------------------/

+iWindow * newPopup_Window (iInt2 screenPos, iWidget *rootWidget);

--

2.25.1

Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/work%2Fv1.7/patch/2d81addf78d6a8b0fb2f2959b04a385c4adffdf2.patch
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
100.033685 milliseconds
Gemini-to-HTML Time
26.166257 milliseconds

This content has been proxied by September (ba2dc).