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) {
delete_Window(i.ptr);
#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) {
pushBack_PtrArray(list, i.ptr);
+}
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
setFreezeDraw_Window(as_Window(d), iTrue);
setFreezeDraw_MainWindow(d->window, iTrue);
savePrefs_App_(d);
saveState_App_(d);
break;
case SDL_APP_TERMINATING:
setFreezeDraw_Window(as_Window(d), iTrue);
setFreezeDraw_MainWindow(d->window, iTrue);
savePrefs_App_(d);
saveState_App_(d);
break;
case SDL_DROPFILE: {
iBool wasUsed = processEvent_MainWindow(d->window, &ev);
iBool wasUsed = processEvent_Window(as_Window(d->window), &ev);
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.type == SDL_USEREVENT && ev.user.code == arrange_UserEventCode) {
printf("[App] rearrange\n");
resize_MainWindow(d->window, -1, -1);
iForIndices(i, d->window->base.roots) {
if (d->window->base.roots[i]) {
d->window->base.roots[i]->pendingArrange = iFalse;
}
}
-// 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();
continue;
}
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
d->window->base.lastHover = d->window->base.hover;
iBool wasUsed = processEvent_MainWindow(d->window, &ev);
/* Per-window processing. */
iBool wasUsed = iFalse;
const iPtrArray *windows = listWindows_App_(d);
iConstForEach(PtrArray, iter, windows) {
iWindow *window = iter.ptr;
setCurrent_Window(window);
window->lastHover = window->hover;
wasUsed = processEvent_Window(window, &ev);
if (ev.type == SDL_MOUSEMOTION) {
break;
}
if (wasUsed) break;
}
setCurrent_Window(d->window);
if (!wasUsed) {
/* There may be a key bindings for this. */
/* There may be a key binding for this. */
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)) {
iForIndices(i, d->window->base.roots) {
iRoot *root = d->window->base.roots[i];
if (root) {
arrange_Widget(root->widget);
}
iConstForEach(PtrArray, iter, windows) {
iWindow *window = iter.ptr;
iForIndices(i, window->roots) {
iRoot *root = window->roots[i];
if (root) {
arrange_Widget(root->widget);
}
}
}
}
if (!wasUsed) {
/* No widget handled the command, so we'll do it. */
setCurrent_Window(d->window);
handleCommand_App(ev.user.data1);
}
/* Allocated by postCommand_Apps(). */
free(ev.user.data1);
}
/* Update when hover has changed. */
if (d->window->base.lastHover != d->window->base.hover) {
refresh_Widget(d->window->base.lastHover);
refresh_Widget(d->window->base.hover);
/* Refresh after hover changes. */ {
iConstForEach(PtrArray, iter, windows) {
iWindow *window = iter.ptr;
if (window->lastHover != window->hover) {
refresh_Widget(window->lastHover);
refresh_Widget(window->hover);
}
}
}
break;
}
@@ -1394,25 +1413,46 @@ static int run_App_(iApp *d) {
void refresh_App(void) {
iApp *d = &app_;
iRoot *root = d->window->base.roots[i];
if (root) {
destroyPending_Root(root);
}
#if defined (LAGRANGE_ENABLE_IDLE_SLEEP)
if (d->warmupFrames == 0 && d->isIdling) {
return;
}
#endif
iConstForEach(PtrArray, j, windows) {
iWindow *win = j.ptr;
setCurrent_Window(win);
iForIndices(i, win->roots) {
iRoot *root = win->roots[i];
if (root) {
destroyPending_Root(root);
}
}
}
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);
iConstForEach(PtrArray, j, windows) {
iWindow *win = j.ptr;
setCurrent_Window(win);
switch (win->type) {
case main_WindowType:
draw_MainWindow(as_MainWindow(win));
break;
default:
draw_Window(win);
break;
}
}
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")) {
iForEach(ObjectList, i,
children_Widget(findChild_Widget(dlg, isUsingPanelLayout_Mobile() ? "panel.top" : "headings"))) {
iForEach(ObjectList,
i,
children_Widget(findChild_Widget(
dlg, isUsingPanelLayout_Mobile() ? "panel.top" : "headings"))) {
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) {
resetFonts_Text(text_Window(win.ptr));
+}
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")) {
resetFonts_Text();
resetFonts_App_(d);
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) {
setFreezeDraw_Window(get_Window(), iTrue);
setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
}
setCStr_String(&d->prefs.symbolFontPath, path);
loadUserFonts_Text();
resetFonts_Text();
resetFonts_App_(d);
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) {
setFreezeDraw_Window(get_Window(), iTrue);
setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
}
d->prefs.font = arg_Command(cmd);
setContentFont_Text(d->prefs.font);
setContentFont_Text(text_Window(d->window), d->prefs.font);
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) {
setFreezeDraw_Window(get_Window(), iTrue);
setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
}
d->prefs.headingFont = arg_Command(cmd);
setHeadingFont_Text(d->prefs.headingFont);
setHeadingFont_Text(text_Window(d->window), d->prefs.headingFont);
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) {
setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */
setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */
}
d->prefs.zoomPercent = arg_Command(cmd);
setContentFontSize_Text((float) d->prefs.zoomPercent / 100.0f);
setContentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);
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) {
setFreezeDraw_Window(get_Window(), iTrue); /* no intermediate draws before docs updated */
setFreezeDraw_MainWindow(get_MainWindow(), iTrue); /* no intermediate draws before docs updated */
}
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);
setContentFontSize_Text((float) d->prefs.zoomPercent / 100.0f);
setContentFontSize_Text(text_Window(d->window), (float) d->prefs.zoomPercent / 100.0f);
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) {
setFreezeDraw_Window(as_Window(d->window), iTrue);
setFreezeDraw_MainWindow(get_MainWindow(), iTrue);
}
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) {
return wm.info.cocoa.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) &&
isEmpty_Range(&d->mark)) {
(deviceType_App() == desktop_AppDeviceType || isEmpty_Range(&d->mark))) {
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) {
d->pendingArrange = iTrue;
SDL_Event ev = { .type = SDL_USEREVENT };
ev.user.code = arrange_UserEventCode;
ev.user.data2 = d;
SDL_PushEvent(&ev);
-}
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) {
text_.contentFont = font;
resetFonts_Text();
+void setContentFont_Text(iText *d, enum iTextFont font) {
d->contentFont = font;
resetFonts_Text(d);
}
}
-void setHeadingFont_Text(enum iTextFont font) {
text_.headingFont = font;
resetFonts_Text();
+void setHeadingFont_Text(iText *d, enum iTextFont font) {
d->headingFont = font;
resetFonts_Text(d);
}
}
-void setContentFontSize_Text(float fontSizeFactor) {
+void setContentFontSize_Text(iText *d, float fontSizeFactor) {
fontSizeFactor *= contentScale_Text_;
iAssert(fontSizeFactor > 0);
text_.contentFontSize = fontSizeFactor;
resetFonts_Text();
d->contentFontSize = fontSizeFactor;
resetFonts_Text(d);
}
}
@@ -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 (text_.cacheBottom > text_.cacheSize.y - maxGlyphHeight_Text_(&text_)) {
if (activeText_->cacheBottom > activeText_->cacheSize.y - maxGlyphHeight_Text_(activeText_)) {
#if !defined (NDEBUG)
printf("[Text] glyph cache is full, clearing!\n"); fflush(stdout);
#endif
resetCache_Text_(&text_);
resetCache_Text_(activeText_);
}
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);
if (match_RegExp(text_.ansiEscape, srcPos, d->source.end - srcPos, &m)) {
if (match_RegExp(activeText_->ansiEscape, srcPos, d->source.end - srcPos, &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);
const int lastCacheBottom = text_.cacheBottom;
const int lastCacheBottom = activeText_->cacheBottom;
iGlyph *glyph = glyphByIndex_Font_(d, glyphIndex);
if (text_.cacheBottom < lastCacheBottom) {
if (activeText_->cacheBottom < lastCacheBottom) {
/* 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_SetSurfacePalette(buf, text_.grayscale);
SDL_SetSurfacePalette(buf, activeText_->grayscale);
}
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_Texture *bufTex = SDL_CreateTextureFromSurface(text_.render, buf);
SDL_Texture *bufTex = SDL_CreateTextureFromSurface(activeText_->render, buf);
SDL_SetTextureBlendMode(bufTex, SDL_BLENDMODE_NONE);
if (!isTargetChanged) {
isTargetChanged = iTrue;
oldTarget = SDL_GetRenderTarget(text_.render);
SDL_SetRenderTarget(text_.render, text_.cache);
oldTarget = SDL_GetRenderTarget(activeText_->render);
SDL_SetRenderTarget(activeText_->render, activeText_->cache);
}
// 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];
SDL_RenderCopy(text_.render,
SDL_RenderCopy(activeText_->render,
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) {
SDL_SetRenderTarget(text_.render, oldTarget);
SDL_SetRenderTarget(activeText_->render, oldTarget);
}
}
@@ -1706,9 +1709,9 @@ static iRect run_Font_(iFont *d, const iRunArgs *args) {
}
if (~mode & permanentColorFlag_RunMode) {
const iColor clr = run->fgColor;
SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b);
if (args->mode & fillBackground_RunMode) {
SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0);
SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0);
}
}
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. */
SDL_RenderFillRect(text_.render, &dst);
SDL_RenderFillRect(activeText_->render, &dst);
}
SDL_RenderCopy(text_.render, text_.cache, &src, &dst);
SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst);
#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);
SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */
SDL_SetTextureBlendMode(activeText_->cache, SDL_BLENDMODE_NONE); /* blended when TextBuf is drawn */
draw_WrapText(wrapText, font, zero_I2(), color | fillBackground_ColorId);
SDL_SetTextureBlendMode(text_.cache, SDL_BLENDMODE_BLEND);
SDL_SetTextureBlendMode(activeText_->cache, SDL_BLENDMODE_BLEND);
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);
SDL_SetRenderDrawColor(text_.render, initial.r, initial.g, initial.b, 0);
SDL_SetRenderDrawColor(activeText_->render, initial.r, initial.g, initial.b, 0);
}
/* 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 (match_RegExp(text_.ansiEscape, chPos, args->text.end - chPos, &m)) {
if (match_RegExp(activeText_->ansiEscape, chPos, args->text.end - chPos, &m)) {
if (mode & draw_RunMode && ~mode & permanentColorFlag_RunMode) {
/* Change the color. */
const iColor clr =
ansiForeground_Color(capturedRange_RegExpMatch(&m, 1), tmParagraph_ColorId);
SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b);
if (args->mode & fillBackground_RunMode) {
SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0);
SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0);
}
}
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);
SDL_SetTextureColorMod(text_.cache, clr.r, clr.g, clr.b);
SDL_SetTextureColorMod(activeText_->cache, clr.r, clr.g, clr.b);
if (args->mode & fillBackground_RunMode) {
SDL_SetRenderDrawColor(text_.render, clr.r, clr.g, clr.b, 0);
SDL_SetRenderDrawColor(activeText_->render, clr.r, clr.g, clr.b, 0);
}
}
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. */
SDL_RenderFillRect(text_.render, &dst);
SDL_RenderFillRect(activeText_->render, &dst);
}
SDL_RenderCopy(text_.render, text_.cache, &src, &dst);
SDL_RenderCopy(activeText_->render, activeText_->cache, &src, &dst);
}
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) {
equal_Command(cmd, "window.focus.gained")) return iTrue;
/* 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);
if (postCommands) {
postCommand_Widget(d, "menu.opened");
}
updateMenuItemFonts_Widget_(d);
iRoot *oldRoot = current_Root();
setFlags_Widget(d, keepOnTop_WidgetFlag, iFalse);
setUserData_Object(d, parent_Widget(d));
removeChild_Widget(parent_Widget(d), d); /* we'll borrow the widget for a while */
iInt2 mousePos;
SDL_GetGlobalMouseState(&mousePos.x, &mousePos.y);
iWindow *win = newPopup_Window(sub_I2(mousePos, divi_I2(gap2_UI, 2)), d);
SDL_SetWindowTitle(win->win, "Menu");
addPopup_App(win); /* window takes the widget */
SDL_ShowWindow(win->win);
draw_Window(win);
setCurrent_Window(mainWindow_App());
setCurrent_Root(oldRoot);
return;
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) {
d->rect.pos = zero_I2(); //neg_I2(bounds_Widget(parent_Widget(d)).pos);
d->rect.pos = zero_I2();
}
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;
bottomExcess += iMax(b, get_Window()->keyboardHeight);
bottomExcess += iMax(b, get_MainWindow()->keyboardHeight);
leftExcess += l;
rightExcess += r;
}
@@ -884,6 +910,18 @@ void closeMenu_Widget(iWidget *d) {
if (d == NULL || flags_Widget(d) & hidden_WidgetFlag) {
return; /* Already closed. */
}
iWindow *win = window_Widget(d);
iAssert(type_Window(win) == popup_WindowType);
iWidget *originalParent = userData_Object(d);
setUserData_Object(d, NULL);
win->roots[0]->widget = NULL;
setRoot_Widget(d, originalParent->root);
addChild_Widget(originalParent, d);
setFlags_Widget(d, keepOnTop_WidgetFlag, iTrue);
SDL_HideWindow(win->win);
collect_Garbage(win, (iDeleteFunc) delete_Window); /* get rid of it after event processing */
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) {
if (get_Window()->focus && get_Window()->focus->root == d->root && isKeyboardEvent_(ev)) {
if (window_Widget(d)->focus && window_Widget(d)->focus->root == d->root && isKeyboardEvent_(ev)) {
/* Root dispatches keyboard events directly to the focused widget. */
if (dispatchEvent_Widget(get_Window()->focus, ev)) {
if (dispatchEvent_Widget(window_Widget(d)->focus, ev)) {
return iTrue;
}
}
@@ -1012,7 +1015,8 @@ iBool dispatchEvent_Widget(iWidget *d, const SDL_Event *ev) {
}
}
else if (ev->type == SDL_MOUSEMOTION &&
(!get_Window()->hover || hasParent_Widget(d, get_Window()->hover)) &&
ev->motion.windowID == SDL_GetWindowID(window_Widget(d)->win) &&
(!window_Widget(d)->hover || hasParent_Widget(d, window_Widget(d)->hover)) &&
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);
if (child == get_Window()->focus && isKeyboardEvent_(ev)) {
if (child == window_Widget(d)->focus && isKeyboardEvent_(ev)) {
continue; /* Already dispatched. */
}
if (isVisible_Widget(child) && child->flags & keepOnTop_WidgetFlag) {
/* Already dispatched. */
/* Already dispatched. */
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) {
printf("[%p] %s:'%s' (on top) ate the motion\n",
printf("[%p] %s:'%s' ate the motion\n",
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);
}
setCursor_Window(get_Window(), SDL_SYSTEM_CURSOR_ARROW);
setCursor_Window(window_Widget(d), SDL_SYSTEM_CURSOR_ARROW);
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:"));
return src == widget || hasParent_Widget(src, widget);
if (src == widget || hasParent_Widget(src, widget)) {
return iTrue;
}
+// 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));
if (type_Window(window_Widget(d)) == popup_WindowType) {
postCommandf_Root(((const iWidget *) d)->root, "cancel popup:1 ptr:%p", d);
d = userData_Object(root_Widget(d));
}
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,
(enum iWindowType type, iRect rect, uint32_t flags),
type, rect, flags)
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) {
if (d->roots[i]) {
setCurrent_Root(d->roots[i]);
delete_Root(d->roots[i]);
d->roots[i] = NULL;
}
}
void deinit_Window(iWindow *d) {
removePopup_App(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) {
if (d->roots[i]) {
setCurrent_Root(d->roots[i]);
deinit_Root(d->roots[i]);
}
-}
void deinit_MainWindow(iMainWindow *d) {
deinitRoots_Window_(as_Window(d));
if (theWindow_ == as_Window(d)) {
theWindow_ = NULL;
}
theMainWindow_ = 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;
resetFonts_Text();
resetFonts_Text(text_Window(d));
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) {
return iFalse;
case SDL_WINDOWEVENT_EXPOSED:
d->isExposed = iTrue;
postRefresh_App();
return iTrue;
case SDL_WINDOWEVENT_RESTORED:
case SDL_WINDOWEVENT_SHOWN:
postRefresh_App();
return iTrue;
case SDL_WINDOWEVENT_FOCUS_LOST:
/* Popup windows are currently only used for menus. */
closeMenu_Widget(d->roots[0]->widget);
return iTrue;
case SDL_WINDOWEVENT_LEAVE:
unhover_Widget();
d->isMouseInside = iFalse;
//postCommand_App("window.mouse.exited");
+// SDL_SetWindowInputFocus(mainWindow_App()->base.win);
printf("mouse leaves popup\n"); fflush(stdout);
//SDL_RaiseWindow(mainWindow_App()->base.win);
postRefresh_App();
return iTrue;
case SDL_WINDOWEVENT_ENTER:
d->isMouseInside = iTrue;
//postCommand_App("window.mouse.entered");
printf("mouse enters popup\n"); fflush(stdout);
return iTrue;
+}
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;
SDL_SetWindowInputFocus(d->base.win);
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. */
invalidate_Window_(d, iTrue);
invalidate_MainWindow_(d, iTrue);
postCommand_App("window.unfreeze");
#endif
return iFalse;
case SDL_WINDOWEVENT_FOCUS_LOST:
postCommand_App("window.focus.lost");
-#if !defined(iPlatformDesktop)
setFreezeDraw_Window(d, iTrue);
+#if !defined (iPlatformDesktop)
setFreezeDraw_MainWindow(d, iTrue);
#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: {
return handleWindowEvent_MainWindow_(d, &ev->window);
if (mw) {
return handleWindowEvent_MainWindow_(mw, &ev->window);
}
else {
return handleWindowEvent_Window_(d, &ev->window);
}
}
case SDL_RENDER_TARGETS_RESET:
case SDL_RENDER_DEVICE_RESET: {
invalidate_MainWindow_(d, iTrue /* force full reset */);
if (mw) {
invalidate_MainWindow_(mw, iTrue /* force full reset */);
}
break;
}
default: {
SDL_Event event = *ev;
if (event.type == SDL_USEREVENT && isCommand_UserEvent(ev, "window.unfreeze")) {
d->base.isDrawFrozen = iFalse;
if (SDL_GetWindowFlags(w->win) & SDL_WINDOW_HIDDEN) {
SDL_ShowWindow(w->win);
mw->isDrawFrozen = iFalse;
if (SDL_GetWindowFlags(d->win) & SDL_WINDOW_HIDDEN) {
SDL_ShowWindow(d->win);
}
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;
}
if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->base.focusGainedAt < 10) {
if (event.type == SDL_KEYDOWN && SDL_GetTicks() - d->focusGainedAt < 10) {
/* 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 */
}
if (event.type == SDL_MOUSEBUTTONDOWN && d->base.ignoreClick) {
d->base.ignoreClick = iFalse;
if (event.type == SDL_MOUSEBUTTONDOWN && d->ignoreClick) {
d->ignoreClick = iFalse;
return iTrue;
}
/* Map mouse pointer coordinate to our coordinate system. */
if (event.type == SDL_MOUSEMOTION) {
setCursor_Window(w, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */
const iInt2 pos = coord_Window(w, event.motion.x, event.motion.y);
setCursor_Window(d, SDL_SYSTEM_CURSOR_ARROW); /* default cursor */
const iInt2 pos = coord_Window(d, event.motion.x, event.motion.y);
event.motion.x = pos.x;
event.motion.y = pos.y;
}
else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
const iInt2 pos = coord_Window(w, event.button.x, event.button.y);
const iInt2 pos = coord_Window(d, event.button.x, event.button.y);
event.button.x = pos.x;
event.button.y = pos.y;
if (event.type == SDL_MOUSEBUTTONDOWN) {
/* Button clicks will change keyroot. */
if (numRoots_Window(w) > 1) {
if (numRoots_Window(d) > 1) {
const iInt2 click = init_I2(event.button.x, event.button.y);
iForIndices(i, w->roots) {
iRoot *root = w->roots[i];
if (root != w->keyRoot && contains_Rect(rect_Root(root), click)) {
setKeyRoot_Window(w, root);
iForIndices(i, d->roots) {
iRoot *root = d->roots[i];
if (root != d->keyRoot && contains_Rect(rect_Root(root), click)) {
setKeyRoot_Window(d, root);
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();
setCurrent_Root(findRoot_Window(w, grabbed));
setCurrent_Root(findRoot_Window(d, grabbed));
wasUsed = dispatchEvent_Widget(grabbed, &event);
}
}
/* Dispatch the event to the tree of widgets. */
if (!wasUsed) {
wasUsed = dispatchEvent_Window(w, &event);
wasUsed = dispatchEvent_Window(d, &event);
}
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();
wasUsed = dispatchEvent_Window(w, &paste);
wasUsed = dispatchEvent_Window(d, &paste);
}
if (event.type == SDL_MOUSEBUTTONDOWN && event.button.button == SDL_BUTTON_RIGHT) {
if (postContextClick_Window(w, &event.button)) {
if (postContextClick_Window(d, &event.button)) {
wasUsed = iTrue;
}
}
}
if (isMetricsChange_UserEvent(&event)) {
iForIndices(i, w->roots) {
updateMetrics_Root(w->roots[i]);
iForIndices(i, d->roots) {
updateMetrics_Root(d->roots[i]);
}
}
if (isCommand_UserEvent(&event, "lang.changed")) {
if (isCommand_UserEvent(&event, "lang.changed") && mw) {
#if defined (iHaveNativeMenus)
/* Retranslate the menus. */
removeMacMenus_();
insertMacMenus_();
#endif
invalidate_Window(w);
iForIndices(i, w->roots) {
if (w->roots[i]) {
updatePreferencesLayout_Widget(findChild_Widget(w->roots[i]->widget, "prefs"));
arrange_Widget(w->roots[i]->widget);
invalidate_Window(d);
iForIndices(i, d->roots) {
if (d->roots[i]) {
updatePreferencesLayout_Widget(findChild_Widget(d->roots[i]->widget, "prefs"));
arrange_Widget(d->roots[i]->widget);
}
}
}
if (event.type == SDL_MOUSEMOTION) {
applyCursor_Window_(w);
applyCursor_Window_(d);
}
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. */
}
if (!root->widget) {
continue;
}
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) {
return;
d->isInvalidated = iFalse;
extern int drawCount_;
drawRoot_Widget(root->widget);
+#if !defined (NDEBUG)
draw_Text(defaultBold_FontId, safeRect_Root(root).pos, red_ColorId, "%d", drawCount_);
drawCount_ = 0;
+#endif
+// drawRectThickness_Paint(&p, (iRect){ zero_I2(), sub_I2(d->size, one_I2()) }, gap_UI / 4, uiSeparator_ColorId);
+}
void draw_MainWindow(iMainWindow *d) {
draw_Window
? */ 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) {
theMainWindow_ = d;
setCurrent_Text(theWindow_->text);
setCurrent_Root(theWindow_->keyRoot);
setCurrent_Text(NULL);
setCurrent_Root(NULL);
+}
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);
setFreezeDraw_Window(w, iTrue);
setFreezeDraw_MainWindow(d, iTrue);
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];
w->keyRoot->window = w;
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) {
new_Window(popup_WindowType,
(iRect){ screenPos, divf_I2(rootWidget->rect.size, get_Window()->pixelRatio) },
SDL_WINDOW_ALWAYS_ON_TOP |
SDL_WINDOW_POPUP_MENU |
SDL_WINDOW_SKIP_TASKBAR);
+#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) {
return ((const iWindow *) d)->type;
}
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
text/plain
This content has been proxied by September (ba2dc).