From e8f5fd342e069643d0fc4b2163713cf8788ca5d6 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Thu, 20 Aug 2020 10:30:15 +0300 Subject: Cleanup --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index ab57900e..3c5b89fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,9 +17,9 @@ message (STATUS "Preparing embedded resources...") # Fonts are too large to comfortably embed as a C source. set (EMBED_IN_EXECUTABLE OFF CACHE BOOL "Embed resources inside the executable") set (EMBED_RESOURCES - res/about.gmi - res/help.gmi - res/version.gmi + res/about/help.gmi + res/about/lagrange.gmi + res/about/version.gmi res/SourceSansPro-Regular.ttf res/FiraSans-Regular.ttf res/FiraSans-Bold.ttf -- cgit v1.2.3 From 3b31ab31eb52578693acee07fdcf84fbeff72707 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 23 Aug 2020 14:30:12 +0300 Subject: Cleanup: Use ".binary" for binary files --- CMakeLists.txt | 4 ++-- Embed.cmake | 4 ++-- src/app.c | 16 ++++++++-------- src/gmcerts.c | 4 ++-- src/ui/macos.m | 4 ++-- 5 files changed, 16 insertions(+), 16 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c5b89fc..5465ae58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,12 +34,12 @@ set (EMBED_RESOURCES endif () embed_make (${EMBED_RESOURCES}) -set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.bin) +set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.binary) set_source_files_properties (${EMB_BIN} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) # Source files. set (SOURCES - ${CMAKE_CURRENT_BINARY_DIR}/resources.bin + ${CMAKE_CURRENT_BINARY_DIR}/resources.binary ${CMAKE_CURRENT_BINARY_DIR}/embedded.c ${CMAKE_CURRENT_BINARY_DIR}/embedded.h src/main.c diff --git a/Embed.cmake b/Embed.cmake index ce2c5ff8..242002f0 100644 --- a/Embed.cmake +++ b/Embed.cmake @@ -4,7 +4,7 @@ option (EMBED_IN_EXECUTABLE "Embed resources inside the executable" OFF) # Note: If disabled, the Unix "cat" tool is required for concatenating -# the resources into a single "resources.bin" file. +# the resources into a single "resources.binary" file. function (embed_getname output fn) get_filename_component (name ${fn} NAME_WE) @@ -78,7 +78,7 @@ function (embed_make) endforeach (fn) else () # Collect resources in a single binary file. - set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.bin) + set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.binary) file (REMOVE ${EMB_BIN}) execute_process (COMMAND cat ${ARGV} OUTPUT_FILE ${EMB_BIN} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) diff --git a/src/app.c b/src/app.c index fe96a1f6..c91366c9 100644 --- a/src/app.c +++ b/src/app.c @@ -60,20 +60,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ iDeclareType(App) #if defined (iPlatformApple) -#define EMB_BIN "../../Resources/resources.bin" +#define EMB_BIN "../../Resources/resources.binary" static const char *dataDir_App_ = "~/Library/Application Support/fi.skyjake.Lagrange"; #endif #if defined (iPlatformMsys) -#define EMB_BIN "../resources.bin" +#define EMB_BIN "../resources.binary" static const char *dataDir_App_ = "~/AppData/Roaming/fi.skyjake.Lagrange"; #endif #if defined (iPlatformLinux) -#define EMB_BIN "../../share/lagrange/resources.bin" -#define EMB_BIN2 "../resources.bin" /* try from build dir as well */ +#define EMB_BIN "../../share/lagrange/resources.binary" static const char *dataDir_App_ = "~/.config/lagrange"; #endif +#define EMB_BIN2 "../resources.binary" /* fallback from build/executable dir */ static const char *prefsFileName_App_ = "prefs.cfg"; -static const char *stateFileName_App_ = "state.bin"; +static const char *stateFileName_App_ = "state.binary"; struct Impl_App { iCommandLine args; @@ -274,9 +274,9 @@ static void init_App_(iApp *d, int argc, char **argv) { load_Bookmarks(d->bookmarks, dataDir_App_); #if defined (iHaveLoadEmbed) /* Load the resources from a file. */ { - if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), "../resources.bin"))) { - if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), EMB_BIN))) { - fprintf(stderr, "failed to load resources.bin: %s\n", strerror(errno)); + if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), EMB_BIN))) { + if (!load_Embed(concatPath_CStr(cstr_String(execPath_App()), EMB_BIN2))) { + fprintf(stderr, "failed to load resources: %s\n", strerror(errno)); exit(-1); } } diff --git a/src/gmcerts.c b/src/gmcerts.c index 58ddfd0c..cc7e9702 100644 --- a/src/gmcerts.c +++ b/src/gmcerts.c @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ static const char *filename_GmCerts_ = "trusted.txt"; static const char *identsDir_GmCerts_ = "idents"; -static const char *identsFilename_GmCerts_ = "idents.bin"; +static const char *identsFilename_GmCerts_ = "idents.binary"; iDeclareClass(TrustEntry) @@ -197,7 +197,6 @@ static void save_GmCerts_(const iGmCerts *d) { deinit_String(&line); } iRelease(f); - saveIdentities_GmCerts_(d); iEndCollect(); } @@ -317,6 +316,7 @@ void init_GmCerts(iGmCerts *d, const char *saveDir) { void deinit_GmCerts(iGmCerts *d) { iGuardMutex(&d->mtx, { + saveIdentities_GmCerts_(d); iForEach(PtrArray, i, &d->idents) { delete_GmIdentity(i.ptr); } diff --git a/src/ui/macos.m b/src/ui/macos.m index 2cb43eae..0e4927ee 100644 --- a/src/ui/macos.m +++ b/src/ui/macos.m @@ -150,8 +150,8 @@ static void appearanceChanged_MacOS_(NSString *name) { const iBool isDark = [name containsString:@"Dark"]; const iBool isHighContrast = [name containsString:@"HighContrast"]; postCommandf_App("os.theme.changed dark:%d contrast:%d", isDark ? 1 : 0, isHighContrast ? 1 : 0); - printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]); - fflush(stdout); +// printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]); +// fflush(stdout); } - (void)setAppearance:(NSString *)name { -- cgit v1.2.3 From d797bdbc82e3770a3a0c0bbbf50a0174436fa798 Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sun, 23 Aug 2020 22:40:31 +0300 Subject: Build option to disable kerning; other optimizations --- CMakeLists.txt | 13 ++++++++++--- Embed.cmake | 6 +++--- src/gmdocument.c | 8 +++++--- src/gmutil.c | 22 +++++++++++++--------- src/ui/documentwidget.c | 3 +++ src/ui/text.c | 9 ++++++++- src/ui/text.h | 1 + 7 files changed, 43 insertions(+), 19 deletions(-) (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 5465ae58..03d253ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,10 @@ project (Lagrange ) set (COPYRIGHT_YEAR 2020) +# Build configuration. +option (ENABLE_KERNING "Enable kerning in font renderer (slower)" ON) +option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) + include (Embed.cmake) find_package (the_Foundation REQUIRED) find_package (PkgConfig REQUIRED) @@ -15,7 +19,6 @@ pkg_check_modules (SDL2 REQUIRED sdl2) # Embedded resources are written to a generated source file. message (STATUS "Preparing embedded resources...") # Fonts are too large to comfortably embed as a C source. -set (EMBED_IN_EXECUTABLE OFF CACHE BOOL "Embed resources inside the executable") set (EMBED_RESOURCES res/about/help.gmi res/about/lagrange.gmi @@ -113,9 +116,13 @@ target_include_directories (app PUBLIC ) target_compile_options (app PUBLIC -Werror=implicit-function-declaration + -Werror=incompatible-pointer-types ${SDL2_CFLAGS} ) target_compile_definitions (app PUBLIC LAGRANGE_APP_VERSION="${PROJECT_VERSION}") +if (ENABLE_KERNING) + target_compile_definitions (app PUBLIC LAGRANGE_ENABLE_KERNING=1) +endif () target_link_libraries (app PUBLIC the_Foundation::the_Foundation) target_link_libraries (app PUBLIC ${SDL2_LDFLAGS}) if (APPLE) @@ -145,7 +152,7 @@ endif () # Deployment. if (MSYS) install (TARGETS app DESTINATION .) - if (NOT EMBED_IN_EXECUTABLE) + if (NOT ENABLE_RESOURCE_EMBED) install (FILES ${EMB_BIN} DESTINATION .) endif () install (PROGRAMS @@ -172,7 +179,7 @@ Icon=fi.skyjake.lagrange") DESTINATION share/icons/hicolor/256x256/apps RENAME fi.skyjake.lagrange.png ) - if (NOT EMBED_IN_EXECUTABLE) + if (NOT ENABLE_RESOURCE_EMBED) install (FILES ${EMB_BIN} DESTINATION share/lagrange) endif () endif () diff --git a/Embed.cmake b/Embed.cmake index 242002f0..f714de02 100644 --- a/Embed.cmake +++ b/Embed.cmake @@ -2,7 +2,7 @@ # Copyright: 2020 Jaakko Keränen # License: BSD 2-Clause -option (EMBED_IN_EXECUTABLE "Embed resources inside the executable" OFF) +option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) # Note: If disabled, the Unix "cat" tool is required for concatenating # the resources into a single "resources.binary" file. @@ -50,7 +50,7 @@ endfunction (embed_write) function (embed_make) set (EMB_H ${CMAKE_CURRENT_BINARY_DIR}/embedded.h) set (EMB_C ${CMAKE_CURRENT_BINARY_DIR}/embedded.c) - if (EMBED_IN_EXECUTABLE) + if (ENABLE_RESOURCE_EMBED) set (needGen NO) if (NOT EXISTS ${EMB_H} OR NOT EXISTS ${EMB_C}) set (needGen YES) @@ -68,7 +68,7 @@ function (embed_make) set (needGen YES) endif () if (needGen) - if (EMBED_IN_EXECUTABLE) + if (ENABLE_RESOURCE_EMBED) # Compose a source file with the resource data in an array. file (WRITE ${EMB_H} "#include \n") file (WRITE ${EMB_C} "#include \"embedded.h\"\n") diff --git a/src/gmdocument.c b/src/gmdocument.c index 42aec9e6..89d567b0 100644 --- a/src/gmdocument.c +++ b/src/gmdocument.c @@ -192,9 +192,12 @@ iInt2 measurePreformattedBlock_GmDocument_(const iGmDocument *d, const char *sta } static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *linkId) { - iRegExp *pattern = new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", caseInsensitive_RegExpOption); + static iRegExp *pattern_; + if (!pattern_) { + pattern_ = new_RegExp("=>\\s*([^\\s]+)(\\s.*)?", caseInsensitive_RegExpOption); + } iRegExpMatch m; - if (matchRange_RegExp(pattern, line, &m)) { + if (matchRange_RegExp(pattern_, line, &m)) { iGmLink *link = new_GmLink(); setRange_String(&link->url, capturedRange_RegExpMatch(&m, 1)); set_String(&link->url, absoluteUrl_String(&d->url, &link->url)); @@ -254,7 +257,6 @@ static iRangecc addLink_GmDocument_(iGmDocument *d, iRangecc line, iGmLinkId *li line = capturedRange_RegExpMatch(&m, 1); /* Show the URL. */ } } - iRelease(pattern); return line; } diff --git a/src/gmutil.c b/src/gmutil.c index d278669d..cd00eb1d 100644 --- a/src/gmutil.c +++ b/src/gmutil.c @@ -27,12 +27,16 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include void init_Url(iUrl *d, const iString *text) { - iRegExp *absPat = - new_RegExp("([a-z]+:)?(//[^/:?]*)(:[0-9]+)?([^?]*)(\\?.*)?", caseInsensitive_RegExpOption); + static iRegExp *absoluteUrlPattern_; + static iRegExp *relativeUrlPattern_; + if (!absoluteUrlPattern_) { + absoluteUrlPattern_ = new_RegExp("([a-z]+:)?(//[^/:?]*)(:[0-9]+)?([^?]*)(\\?.*)?", + caseInsensitive_RegExpOption); + } iRegExpMatch m; - if (matchString_RegExp(absPat, text, &m)) { + if (matchString_RegExp(absoluteUrlPattern_, text, &m)) { d->protocol = capturedRange_RegExpMatch(&m, 1); - d->host = capturedRange_RegExpMatch(&m, 2); + d->host = capturedRange_RegExpMatch(&m, 2); if (!isEmpty_Range(&d->host)) { d->host.start += 2; /* skip the double slash */ } @@ -40,21 +44,21 @@ void init_Url(iUrl *d, const iString *text) { if (!isEmpty_Range(&d->port)) { d->port.start++; /* omit the colon */ } - d->path = capturedRange_RegExpMatch(&m, 4); + d->path = capturedRange_RegExpMatch(&m, 4); d->query = capturedRange_RegExpMatch(&m, 5); } else { /* Must be a relative path. */ iZap(*d); - iRegExp *relPat = new_RegExp("([a-z]+:)?([^?]*)(\\?.*)?", 0); - if (matchString_RegExp(relPat, text, &m)) { + if (!relativeUrlPattern_) { + relativeUrlPattern_ = new_RegExp("([a-z]+:)?([^?]*)(\\?.*)?", 0); + } + if (matchString_RegExp(relativeUrlPattern_, text, &m)) { d->protocol = capturedRange_RegExpMatch(&m, 1); d->path = capturedRange_RegExpMatch(&m, 2); d->query = capturedRange_RegExpMatch(&m, 3); } - iRelease(relPat); } - iRelease(absPat); if (!isEmpty_Range(&d->protocol)) { d->protocol.end--; /* omit the colon */ } diff --git a/src/ui/documentwidget.c b/src/ui/documentwidget.c index 45bd4868..c39d9f12 100644 --- a/src/ui/documentwidget.c +++ b/src/ui/documentwidget.c @@ -1731,11 +1731,14 @@ static void draw_DocumentWidget_(const iDocumentWidget *d) { visBuf->index = vbDst; } /* Dynamic content. */ { + extern int enableKerning_Text; + enableKerning_Text = iFalse; /* need to be fast, these is redone on every redraw */ iPaint *p = &ctxDynamic.paint; init_Paint(p); setClip_Paint(p, bounds); render_GmDocument(d->doc, visRange, drawRun_DrawContext_, &ctxDynamic); unsetClip_Paint(p); + enableKerning_Text = iTrue; } // drawRect_Paint(&ctx.paint, diff --git a/src/ui/text.c b/src/ui/text.c index b4b7fb0c..40956e29 100644 --- a/src/ui/text.c +++ b/src/ui/text.c @@ -48,6 +48,7 @@ iDeclareTypeConstructionArgs(Glyph, iChar ch) int gap_Text; /* cf. gap_UI in metrics.h */ int enableHalfPixelGlyphs_Text = iTrue; /* debug setting */ +int enableKerning_Text = iTrue; /* looking up kern pairs is slow */ struct Impl_Glyph { iHashNode node; @@ -85,6 +86,7 @@ struct Impl_Font { int baseline; iHash glyphs; iBool isMonospaced; + iBool manualKernOnly; enum iFontId symbolsFont; /* font to use for symbols */ }; @@ -176,6 +178,9 @@ static void initFonts_Text_(iText *d) { if (fontData[i].ttf == &fontFiraMonoRegular_Embedded) { font->isMonospaced = iTrue; } + if (i == default_FontId || i == defaultMedium_FontId) { + font->manualKernOnly = iTrue; + } } gap_Text = iRound(gap_UI * d->contentFontSize); } @@ -511,9 +516,11 @@ static iRect run_Font_(iFont *d, enum iRunMode mode, iRangecc text, size_t maxLe /* Manual kerning for double-slash. */ xpos -= glyph->rect[hoff].size.x * 0.5f; } - else if (next) { +#if defined (LAGRANGE_ENABLE_KERNING) + else if (enableKerning_Text && !d->manualKernOnly && next) { xpos += d->scale * stbtt_GetCodepointKernAdvance(&d->font, ch, next); } +#endif } prevCh = ch; if (--maxLen == 0) { diff --git a/src/ui/text.h b/src/ui/text.h index edd7ed4e..2b4ec5c3 100644 --- a/src/ui/text.h +++ b/src/ui/text.h @@ -58,6 +58,7 @@ enum iFontId { hugeEmoji_FontId, smallEmoji_FontId, max_FontId, + /* Meta: */ fromSymbolsToEmojiOffset_FontId = 7, /* UI fonts: */ -- cgit v1.2.3 From f90104259c4929c86c1af630c4eff62580c3028b Mon Sep 17 00:00:00 2001 From: Jaakko Keränen Date: Sat, 29 Aug 2020 08:26:22 +0300 Subject: Moved macOS sources to parent dir --- CMakeLists.txt | 2 +- src/app.c | 2 +- src/macos.h | 31 ++++ src/macos.m | 502 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/ui/macos.h | 31 ---- src/ui/macos.m | 502 --------------------------------------------------------- 6 files changed, 535 insertions(+), 535 deletions(-) create mode 100644 src/macos.h create mode 100644 src/macos.m delete mode 100644 src/ui/macos.h delete mode 100644 src/ui/macos.m (limited to 'CMakeLists.txt') diff --git a/CMakeLists.txt b/CMakeLists.txt index 03d253ea..a5b41da3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,7 @@ set (SOURCES ) if (IOS) elseif (APPLE) - list (APPEND SOURCES src/ui/macos.m src/ui/macos.h) + list (APPEND SOURCES src/macos.m src/macos.h) list (APPEND RESOURCES "res/Lagrange.icns") endif () if (MSYS) diff --git a/src/app.c b/src/app.c index b8909306..e7f31dbe 100644 --- a/src/app.c +++ b/src/app.c @@ -55,7 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #if defined (iPlatformApple) && !defined (iPlatformIOS) -# include "ui/macos.h" +# include "macos.h" #endif iDeclareType(App) diff --git a/src/macos.h b/src/macos.h new file mode 100644 index 00000000..07990090 --- /dev/null +++ b/src/macos.h @@ -0,0 +1,31 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#pragma once + +#include "ui/util.h" + +/* Platform-specific functionality for macOS */ + +void setupApplication_MacOS (void); +void insertMenuItems_MacOS (const char *menuLabel, int atIndex, const iMenuItem *items, size_t count); +void handleCommand_MacOS (const char *cmd); diff --git a/src/macos.m b/src/macos.m new file mode 100644 index 00000000..edbb6df0 --- /dev/null +++ b/src/macos.m @@ -0,0 +1,502 @@ +/* Copyright 2020 Jaakko Keränen + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "macos.h" +#include "app.h" +#include "ui/command.h" +#include "ui/widget.h" +#include "ui/color.h" + +#import + +#if 0 +static NSTouchBarItemIdentifier play_TouchId_ = @"fi.skyjake.BitwiseHarmony.play"; +static NSTouchBarItemIdentifier restart_TouchId_ = @"fi.skyjake.BitwiseHarmony.restart"; + +static NSTouchBarItemIdentifier seqMoveUp_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.up"; +static NSTouchBarItemIdentifier seqMoveDown_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.down"; + +static NSTouchBarItemIdentifier goto_TouchId_ = @"fi.skyjake.BitwiseHarmony.goto"; +static NSTouchBarItemIdentifier mute_TouchId_ = @"fi.skyjake.BitwiseHarmony.mute"; +static NSTouchBarItemIdentifier solo_TouchId_ = @"fi.skyjake.BitwiseHarmony.solo"; +static NSTouchBarItemIdentifier color_TouchId_ = @"fi.skyjake.BitwiseHarmony.color"; +static NSTouchBarItemIdentifier event_TouchId_ = @"fi.skyjake.BitwiseHarmony.event"; + +static NSTouchBarItemIdentifier eventList_TouchId_ = @"fi.skyjake.BitwiseHarmony.eventlist"; +static NSTouchBarItemIdentifier masterGainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.mastergain"; +static NSTouchBarItemIdentifier resetEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.reset"; +static NSTouchBarItemIdentifier voiceEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.voice"; +static NSTouchBarItemIdentifier panEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pan"; +static NSTouchBarItemIdentifier gainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.gain"; +static NSTouchBarItemIdentifier fadeEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.fade"; +static NSTouchBarItemIdentifier pitchSpeedEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchspeed"; +static NSTouchBarItemIdentifier pitchBendUpEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbendup"; +static NSTouchBarItemIdentifier pitchBendDownEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbenddown"; +static NSTouchBarItemIdentifier tremoloEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.tremolo"; +#endif + +enum iTouchBarVariant { + default_TouchBarVariant, +}; + +@interface CommandButton : NSButtonTouchBarItem { + NSString *command; + iWidget *widget; +} +- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier + title:(NSString *)title + command:(NSString *)cmd; +- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier + title:(NSString *)title + widget:(iWidget *)widget + command:(NSString *)cmd; +- (void)dealloc; +@end + +@implementation CommandButton + +- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier + title:(NSString *)title + command:(NSString *)cmd { + [super initWithIdentifier:identifier]; + self.title = title; + self.target = self; + self.action = @selector(buttonPressed); + command = cmd; + return self; +} + +- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier + title:(NSString *)title + widget:(iWidget *)aWidget + command:(NSString *)cmd { + [self initWithIdentifier:identifier title:title command:[cmd retain]]; + widget = aWidget; + return self; +} + +- (void)dealloc { + [command release]; + [super dealloc]; +} + +- (void)buttonPressed { + const char *cmd = [command cStringUsingEncoding:NSUTF8StringEncoding]; + if (widget) { + postCommand_Widget(widget, "%s", cmd); + } + else { + postCommand_App(cmd); + } +} + +@end + +@interface MyDelegate : NSResponder { + enum iTouchBarVariant touchBarVariant; + NSString *currentAppearanceName; + NSObject *sdlDelegate; + NSMutableDictionary *menuCommands; +} +- (id)initWithSDLDelegate:(NSObject *)sdl; +//- (NSTouchBar *)makeTouchBar; +/* SDL needs to do its own thing. */ +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; +- (void)applicationDidFinishLaunching:(NSNotification *)notifications; +@end + +@implementation MyDelegate + +- (id)initWithSDLDelegate:(NSObject *)sdl { + [super init]; + currentAppearanceName = nil; + menuCommands = [[NSMutableDictionary alloc] init]; + touchBarVariant = default_TouchBarVariant; + sdlDelegate = sdl; + return self; +} + +- (void)dealloc { + [menuCommands release]; + [currentAppearanceName release]; + [super dealloc]; +} + +- (void)setTouchBarVariant:(enum iTouchBarVariant)variant { + touchBarVariant = variant; + self.touchBar = nil; +} + +static void appearanceChanged_MacOS_(NSString *name) { + const iBool isDark = [name containsString:@"Dark"]; + const iBool isHighContrast = [name containsString:@"HighContrast"]; + postCommandf_App("os.theme.changed dark:%d contrast:%d", isDark ? 1 : 0, isHighContrast ? 1 : 0); +// printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]); +// fflush(stdout); +} + +- (void)setAppearance:(NSString *)name { + if (!currentAppearanceName || ![name isEqualToString:currentAppearanceName]) { + if (currentAppearanceName) { + [currentAppearanceName release]; + } + currentAppearanceName = [name retain]; + appearanceChanged_MacOS_(currentAppearanceName); + } +} + +- (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem { + [menuCommands setObject:command forKey:[menuItem title]]; +} + +- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { + return [sdlDelegate application:theApplication openFile:filename]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + [sdlDelegate applicationDidFinishLaunching:notification]; +} + +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + iUnused(object, change); + if ([keyPath isEqualToString:@"effectiveAppearance"] && context == self) { + [self setAppearance:[[NSApp effectiveAppearance] name]]; + } +} + +#if 0 +- (NSTouchBar *)makeTouchBar { + NSTouchBar *bar = [[NSTouchBar alloc] init]; + bar.delegate = self; + switch (touchBarVariant) { + case default_TouchBarVariant: + bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, + NSTouchBarItemIdentifierFixedSpaceSmall, + NSTouchBarItemIdentifierOtherItemsProxy ]; + break; + case sequence_TouchBarVariant: + bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + seqMoveUp_TouchId_, seqMoveDown_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + NSTouchBarItemIdentifierOtherItemsProxy]; + break; + case tracker_TouchBarVariant: + bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + goto_TouchId_, + event_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + solo_TouchId_, mute_TouchId_, color_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + NSTouchBarItemIdentifierOtherItemsProxy ]; + break; + case wide_TouchBarVariant: + bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + event_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + solo_TouchId_, mute_TouchId_, color_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + seqMoveUp_TouchId_, seqMoveDown_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace, + NSTouchBarItemIdentifierOtherItemsProxy ]; + break; + } + return bar; +} +#endif + +- (void)showPreferences { + postCommand_App("preferences"); +} + +- (void)closeTab { + postCommand_App("tabs.close"); +} + +- (void)postMenuItemCommand:(id)sender { + NSString *command = [menuCommands objectForKey:[(NSMenuItem *)sender title]]; + if (command) { + postCommand_App([command cStringUsingEncoding:NSUTF8StringEncoding]); + } +} + +- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar + makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier { + iUnused(touchBar); +#if 0 + if ([identifier isEqualToString:play_TouchId_]) { + return [NSButtonTouchBarItem + buttonTouchBarItemWithIdentifier:identifier + image:[NSImage imageNamed:NSImageNameTouchBarPlayPauseTemplate] + target:self + action:@selector(playPressed)]; + } + else if ([identifier isEqualToString:restart_TouchId_]) { + return [NSButtonTouchBarItem + buttonTouchBarItemWithIdentifier:identifier + image:[NSImage imageNamed:NSImageNameTouchBarSkipToStartTemplate] + target:self + action:@selector(restartPressed)]; + } + else if ([identifier isEqualToString:seqMoveUp_TouchId_]) { + return [[CommandButton alloc] initWithIdentifier:identifier + title:@"Seq\u2b06" + widget:findWidget_App("sequence") + command:@"sequence.swap arg:-1"]; + } + else if ([identifier isEqualToString:seqMoveDown_TouchId_]) { + return [[CommandButton alloc] initWithIdentifier:identifier + title:@"Seq\u2b07" + widget:findWidget_App("sequence") + command:@"sequence.swap arg:1"]; + } + else if ([identifier isEqualToString:goto_TouchId_]) { + return [[CommandButton alloc] initWithIdentifier:identifier + title:@"Go to…" + command:@"pattern.goto arg:-1"]; + } + else if ([identifier isEqualToString:event_TouchId_]) { + NSTouchBar *events = [[NSTouchBar alloc] init]; + events.delegate = self; + events.defaultItemIdentifiers = @[ eventList_TouchId_ ]; + NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier]; + pop.collapsedRepresentationLabel = @"Event"; + pop.popoverTouchBar = events; + [events release]; + return pop; + } + else if ([identifier isEqualToString:eventList_TouchId_]) { + const struct { + NSTouchBarItemIdentifier id; + const char *title; + const char *command; + } buttonDefs_[] = { + { voiceEvent_TouchId_, "Voice", "tracker.setevent type:2" }, + { panEvent_TouchId_, "Pan", "tracker.setevent type:3 arg:128" }, + { gainEvent_TouchId_, "Gain", "tracker.setevent type:4 arg:128" }, + { fadeEvent_TouchId_, "Fade", "tracker.setevent type:5" }, + { tremoloEvent_TouchId_, "Trem", "tracker.setevent type:9" }, + { pitchSpeedEvent_TouchId_, "P.Spd", "tracker.setevent type:6" }, + { pitchBendUpEvent_TouchId_, "BnUp", "tracker.setevent type:7" }, + { pitchBendDownEvent_TouchId_, "BnDn", "tracker.setevent type:8" }, + { masterGainEvent_TouchId_, "M.Gain", "tracker.setevent type:10 arg:64" }, + { resetEvent_TouchId_, "Reset", "tracker.setevent type:1" }, + }; + NSMutableArray *items = [[NSMutableArray alloc] init]; + iForIndices(i, buttonDefs_) { + CommandButton *button = [[CommandButton alloc] + initWithIdentifier:buttonDefs_[i].id + title:[NSString stringWithUTF8String:buttonDefs_[i].title] + widget:findWidget_App("tracker") + command:[NSString stringWithUTF8String:buttonDefs_[i].command] + ]; + [items addObject:button]; + } + NSGroupTouchBarItem *group = [NSGroupTouchBarItem groupItemWithIdentifier:identifier + items:items]; + [items release]; + return group; + } + else if ([identifier isEqualToString:mute_TouchId_]) { + return [[CommandButton alloc] initWithIdentifier:identifier + title:@"Mute" + widget:findWidget_App("tracker") + command:@"tracker.mute"]; + } + else if ([identifier isEqualToString:solo_TouchId_]) { + return [[CommandButton alloc] initWithIdentifier:identifier + title:@"Solo" + widget:findWidget_App("tracker") + command:@"tracker.solo"]; + } + else if ([identifier isEqualToString:color_TouchId_]) { + NSTouchBar *colors = [[NSTouchBar alloc] init]; + colors.delegate = self; + colors.defaultItemIdentifiers = @[ NSTouchBarItemIdentifierFlexibleSpace, + whiteColor_TouchId_, + yellowColor_TouchId_, + orangeColor_TouchId_, + redColor_TouchId_, + magentaColor_TouchId_, + blueColor_TouchId_, + cyanColor_TouchId_, + greenColor_TouchId_, + NSTouchBarItemIdentifierFlexibleSpace ]; + NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier]; + pop.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarColorPickerFill]; + pop.popoverTouchBar = colors; + [colors release]; + return pop; + } + else if ([identifier isEqualToString:whiteColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:white_TrackColor]; + } + else if ([identifier isEqualToString:yellowColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:yellow_TrackColor]; + } + else if ([identifier isEqualToString:orangeColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:orange_TrackColor]; + } + else if ([identifier isEqualToString:redColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:red_TrackColor]; + } + else if ([identifier isEqualToString:magentaColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:magenta_TrackColor]; + } + else if ([identifier isEqualToString:blueColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:blue_TrackColor]; + } + else if ([identifier isEqualToString:cyanColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:cyan_TrackColor]; + } + else if ([identifier isEqualToString:greenColor_TouchId_]) { + return [[ColorButton alloc] initWithIdentifier:identifier + trackColor:green_TrackColor]; + } +#endif + return nil; +} + +@end + +void enableMomentumScroll_MacOS(void) { + [[NSUserDefaults standardUserDefaults] setBool: YES + forKey: @"AppleMomentumScrollSupported"]; +} + +void setupApplication_MacOS(void) { + NSApplication *app = [NSApplication sharedApplication]; + //appearanceChanged_MacOS_([[app effectiveAppearance] name]); + /* Our delegate will override SDL's delegate. */ + MyDelegate *myDel = [[MyDelegate alloc] initWithSDLDelegate:app.delegate]; + [myDel setAppearance:[[app effectiveAppearance] name]]; + app.delegate = myDel; + NSMenu *appMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; + NSMenuItem *prefsItem = [appMenu itemWithTitle:@"Preferences…"]; + prefsItem.target = myDel; + prefsItem.action = @selector(showPreferences); + /* Get rid of the default window close item */ + NSMenu *windowMenu = [[[NSApp mainMenu] itemWithTitle:@"Window"] submenu]; + NSMenuItem *windowCloseItem = [windowMenu itemWithTitle:@"Close"]; + windowCloseItem.target = myDel; + windowCloseItem.action = @selector(closeTab); +} + +void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem *items, size_t count) { + NSApplication *app = [NSApplication sharedApplication]; + MyDelegate *myDel = (MyDelegate *) app.delegate; + [app addObserver:myDel + forKeyPath:@"effectiveAppearance" + options:0 + context:myDel]; + NSMenu *appMenu = [app mainMenu]; + NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] + action:nil + keyEquivalent:@"" + atIndex:atIndex]; + NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; + for (size_t i = 0; i < count; ++i) { + const char *label = items[i].label; + if (label[0] == '\r') { + /* Skip the formatting escape. */ + label += 2; + } + if (equal_CStr(label, "---")) { + [menu addItem:[NSMenuItem separatorItem]]; + } + else { + const iBool hasCommand = (items[i].command && items[i].command[0]); + iString key; + init_String(&key); + if (items[i].key == SDLK_LEFT) { + appendChar_String(&key, 0x2190); + } + else if (items[i].key == SDLK_RIGHT) { + appendChar_String(&key, 0x2192); + } + else if (items[i].key) { + appendChar_String(&key, items[i].key); + } + NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:label] + action:(hasCommand ? @selector(postMenuItemCommand:) : nil) + keyEquivalent:[NSString stringWithUTF8String:cstr_String(&key)]]; + NSEventModifierFlags modMask = 0; + if (items[i].kmods & KMOD_GUI) { + modMask |= NSEventModifierFlagCommand; + } + if (items[i].kmods & KMOD_ALT) { + modMask |= NSEventModifierFlagOption; + } + if (items[i].kmods & KMOD_CTRL) { + modMask |= NSEventModifierFlagControl; + } + if (items[i].kmods & KMOD_SHIFT) { + modMask |= NSEventModifierFlagShift; + } + [item setKeyEquivalentModifierMask:modMask]; + if (hasCommand) { + [myDel setCommand:[NSString stringWithUTF8String:items[i].command] forMenuItem:item]; + } + deinit_String(&key); + } + } + [mainItem setSubmenu:menu]; + [menu release]; +} + +void handleCommand_MacOS(const char *cmd) { + if (equal_Command(cmd, "prefs.ostheme.changed")) { + if (arg_Command(cmd)) { + appearanceChanged_MacOS_([[NSApp effectiveAppearance] name]); + } + } +#if 0 + if (equal_Command(cmd, "tabs.changed")) { + MyDelegate *myDel = (MyDelegate *) [[NSApplication sharedApplication] delegate]; + const char *tabId = suffixPtr_Command(cmd, "id"); + if (equal_CStr(tabId, "tracker")) { + [myDel setTouchBarVariant:tracker_TouchBarVariant]; + } + else if (equal_CStr(tabId, "sequence")) { + [myDel setTouchBarVariant:sequence_TouchBarVariant]; + } + else if (equal_CStr(tabId, "trackertab")) { + [myDel setTouchBarVariant:wide_TouchBarVariant]; + } + else { + [myDel setTouchBarVariant:default_TouchBarVariant]; + } + } +#endif +} diff --git a/src/ui/macos.h b/src/ui/macos.h deleted file mode 100644 index 9369b018..00000000 --- a/src/ui/macos.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2020 Jaakko Keränen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#pragma once - -#include "util.h" - -/* Platform-specific functionality for macOS */ - -void setupApplication_MacOS (void); -void insertMenuItems_MacOS (const char *menuLabel, int atIndex, const iMenuItem *items, size_t count); -void handleCommand_MacOS (const char *cmd); diff --git a/src/ui/macos.m b/src/ui/macos.m deleted file mode 100644 index 593c3810..00000000 --- a/src/ui/macos.m +++ /dev/null @@ -1,502 +0,0 @@ -/* Copyright 2020 Jaakko Keränen - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - -#include "macos.h" -#include "app.h" -#include "command.h" -#include "widget.h" -#include "color.h" - -#import - -#if 0 -static NSTouchBarItemIdentifier play_TouchId_ = @"fi.skyjake.BitwiseHarmony.play"; -static NSTouchBarItemIdentifier restart_TouchId_ = @"fi.skyjake.BitwiseHarmony.restart"; - -static NSTouchBarItemIdentifier seqMoveUp_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.up"; -static NSTouchBarItemIdentifier seqMoveDown_TouchId_ = @"fi.skyjake.BitwiseHarmony.sequence.move.down"; - -static NSTouchBarItemIdentifier goto_TouchId_ = @"fi.skyjake.BitwiseHarmony.goto"; -static NSTouchBarItemIdentifier mute_TouchId_ = @"fi.skyjake.BitwiseHarmony.mute"; -static NSTouchBarItemIdentifier solo_TouchId_ = @"fi.skyjake.BitwiseHarmony.solo"; -static NSTouchBarItemIdentifier color_TouchId_ = @"fi.skyjake.BitwiseHarmony.color"; -static NSTouchBarItemIdentifier event_TouchId_ = @"fi.skyjake.BitwiseHarmony.event"; - -static NSTouchBarItemIdentifier eventList_TouchId_ = @"fi.skyjake.BitwiseHarmony.eventlist"; -static NSTouchBarItemIdentifier masterGainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.mastergain"; -static NSTouchBarItemIdentifier resetEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.reset"; -static NSTouchBarItemIdentifier voiceEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.voice"; -static NSTouchBarItemIdentifier panEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pan"; -static NSTouchBarItemIdentifier gainEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.gain"; -static NSTouchBarItemIdentifier fadeEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.fade"; -static NSTouchBarItemIdentifier pitchSpeedEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchspeed"; -static NSTouchBarItemIdentifier pitchBendUpEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbendup"; -static NSTouchBarItemIdentifier pitchBendDownEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.pitchbenddown"; -static NSTouchBarItemIdentifier tremoloEvent_TouchId_ = @"fi.skyjake.BitwiseHarmony.event.tremolo"; -#endif - -enum iTouchBarVariant { - default_TouchBarVariant, -}; - -@interface CommandButton : NSButtonTouchBarItem { - NSString *command; - iWidget *widget; -} -- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier - title:(NSString *)title - command:(NSString *)cmd; -- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier - title:(NSString *)title - widget:(iWidget *)widget - command:(NSString *)cmd; -- (void)dealloc; -@end - -@implementation CommandButton - -- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier - title:(NSString *)title - command:(NSString *)cmd { - [super initWithIdentifier:identifier]; - self.title = title; - self.target = self; - self.action = @selector(buttonPressed); - command = cmd; - return self; -} - -- (id)initWithIdentifier:(NSTouchBarItemIdentifier)identifier - title:(NSString *)title - widget:(iWidget *)aWidget - command:(NSString *)cmd { - [self initWithIdentifier:identifier title:title command:[cmd retain]]; - widget = aWidget; - return self; -} - -- (void)dealloc { - [command release]; - [super dealloc]; -} - -- (void)buttonPressed { - const char *cmd = [command cStringUsingEncoding:NSUTF8StringEncoding]; - if (widget) { - postCommand_Widget(widget, "%s", cmd); - } - else { - postCommand_App(cmd); - } -} - -@end - -@interface MyDelegate : NSResponder { - enum iTouchBarVariant touchBarVariant; - NSString *currentAppearanceName; - NSObject *sdlDelegate; - NSMutableDictionary *menuCommands; -} -- (id)initWithSDLDelegate:(NSObject *)sdl; -//- (NSTouchBar *)makeTouchBar; -/* SDL needs to do its own thing. */ -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename; -- (void)applicationDidFinishLaunching:(NSNotification *)notifications; -@end - -@implementation MyDelegate - -- (id)initWithSDLDelegate:(NSObject *)sdl { - [super init]; - currentAppearanceName = nil; - menuCommands = [[NSMutableDictionary alloc] init]; - touchBarVariant = default_TouchBarVariant; - sdlDelegate = sdl; - return self; -} - -- (void)dealloc { - [menuCommands release]; - [currentAppearanceName release]; - [super dealloc]; -} - -- (void)setTouchBarVariant:(enum iTouchBarVariant)variant { - touchBarVariant = variant; - self.touchBar = nil; -} - -static void appearanceChanged_MacOS_(NSString *name) { - const iBool isDark = [name containsString:@"Dark"]; - const iBool isHighContrast = [name containsString:@"HighContrast"]; - postCommandf_App("os.theme.changed dark:%d contrast:%d", isDark ? 1 : 0, isHighContrast ? 1 : 0); -// printf("Effective appearance changed: %s\n", [name cStringUsingEncoding:NSUTF8StringEncoding]); -// fflush(stdout); -} - -- (void)setAppearance:(NSString *)name { - if (!currentAppearanceName || ![name isEqualToString:currentAppearanceName]) { - if (currentAppearanceName) { - [currentAppearanceName release]; - } - currentAppearanceName = [name retain]; - appearanceChanged_MacOS_(currentAppearanceName); - } -} - -- (void)setCommand:(NSString *)command forMenuItem:(NSMenuItem *)menuItem { - [menuCommands setObject:command forKey:[menuItem title]]; -} - -- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename { - return [sdlDelegate application:theApplication openFile:filename]; -} - -- (void)applicationDidFinishLaunching:(NSNotification *)notification { - [sdlDelegate applicationDidFinishLaunching:notification]; -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context { - iUnused(object, change); - if ([keyPath isEqualToString:@"effectiveAppearance"] && context == self) { - [self setAppearance:[[NSApp effectiveAppearance] name]]; - } -} - -#if 0 -- (NSTouchBar *)makeTouchBar { - NSTouchBar *bar = [[NSTouchBar alloc] init]; - bar.delegate = self; - switch (touchBarVariant) { - case default_TouchBarVariant: - bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, - NSTouchBarItemIdentifierFixedSpaceSmall, - NSTouchBarItemIdentifierOtherItemsProxy ]; - break; - case sequence_TouchBarVariant: - bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - seqMoveUp_TouchId_, seqMoveDown_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - NSTouchBarItemIdentifierOtherItemsProxy]; - break; - case tracker_TouchBarVariant: - bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - goto_TouchId_, - event_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - solo_TouchId_, mute_TouchId_, color_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - NSTouchBarItemIdentifierOtherItemsProxy ]; - break; - case wide_TouchBarVariant: - bar.defaultItemIdentifiers = @[ play_TouchId_, restart_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - event_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - solo_TouchId_, mute_TouchId_, color_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - seqMoveUp_TouchId_, seqMoveDown_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace, - NSTouchBarItemIdentifierOtherItemsProxy ]; - break; - } - return bar; -} -#endif - -- (void)showPreferences { - postCommand_App("preferences"); -} - -- (void)closeTab { - postCommand_App("tabs.close"); -} - -- (void)postMenuItemCommand:(id)sender { - NSString *command = [menuCommands objectForKey:[(NSMenuItem *)sender title]]; - if (command) { - postCommand_App([command cStringUsingEncoding:NSUTF8StringEncoding]); - } -} - -- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar - makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier { - iUnused(touchBar); -#if 0 - if ([identifier isEqualToString:play_TouchId_]) { - return [NSButtonTouchBarItem - buttonTouchBarItemWithIdentifier:identifier - image:[NSImage imageNamed:NSImageNameTouchBarPlayPauseTemplate] - target:self - action:@selector(playPressed)]; - } - else if ([identifier isEqualToString:restart_TouchId_]) { - return [NSButtonTouchBarItem - buttonTouchBarItemWithIdentifier:identifier - image:[NSImage imageNamed:NSImageNameTouchBarSkipToStartTemplate] - target:self - action:@selector(restartPressed)]; - } - else if ([identifier isEqualToString:seqMoveUp_TouchId_]) { - return [[CommandButton alloc] initWithIdentifier:identifier - title:@"Seq\u2b06" - widget:findWidget_App("sequence") - command:@"sequence.swap arg:-1"]; - } - else if ([identifier isEqualToString:seqMoveDown_TouchId_]) { - return [[CommandButton alloc] initWithIdentifier:identifier - title:@"Seq\u2b07" - widget:findWidget_App("sequence") - command:@"sequence.swap arg:1"]; - } - else if ([identifier isEqualToString:goto_TouchId_]) { - return [[CommandButton alloc] initWithIdentifier:identifier - title:@"Go to…" - command:@"pattern.goto arg:-1"]; - } - else if ([identifier isEqualToString:event_TouchId_]) { - NSTouchBar *events = [[NSTouchBar alloc] init]; - events.delegate = self; - events.defaultItemIdentifiers = @[ eventList_TouchId_ ]; - NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier]; - pop.collapsedRepresentationLabel = @"Event"; - pop.popoverTouchBar = events; - [events release]; - return pop; - } - else if ([identifier isEqualToString:eventList_TouchId_]) { - const struct { - NSTouchBarItemIdentifier id; - const char *title; - const char *command; - } buttonDefs_[] = { - { voiceEvent_TouchId_, "Voice", "tracker.setevent type:2" }, - { panEvent_TouchId_, "Pan", "tracker.setevent type:3 arg:128" }, - { gainEvent_TouchId_, "Gain", "tracker.setevent type:4 arg:128" }, - { fadeEvent_TouchId_, "Fade", "tracker.setevent type:5" }, - { tremoloEvent_TouchId_, "Trem", "tracker.setevent type:9" }, - { pitchSpeedEvent_TouchId_, "P.Spd", "tracker.setevent type:6" }, - { pitchBendUpEvent_TouchId_, "BnUp", "tracker.setevent type:7" }, - { pitchBendDownEvent_TouchId_, "BnDn", "tracker.setevent type:8" }, - { masterGainEvent_TouchId_, "M.Gain", "tracker.setevent type:10 arg:64" }, - { resetEvent_TouchId_, "Reset", "tracker.setevent type:1" }, - }; - NSMutableArray *items = [[NSMutableArray alloc] init]; - iForIndices(i, buttonDefs_) { - CommandButton *button = [[CommandButton alloc] - initWithIdentifier:buttonDefs_[i].id - title:[NSString stringWithUTF8String:buttonDefs_[i].title] - widget:findWidget_App("tracker") - command:[NSString stringWithUTF8String:buttonDefs_[i].command] - ]; - [items addObject:button]; - } - NSGroupTouchBarItem *group = [NSGroupTouchBarItem groupItemWithIdentifier:identifier - items:items]; - [items release]; - return group; - } - else if ([identifier isEqualToString:mute_TouchId_]) { - return [[CommandButton alloc] initWithIdentifier:identifier - title:@"Mute" - widget:findWidget_App("tracker") - command:@"tracker.mute"]; - } - else if ([identifier isEqualToString:solo_TouchId_]) { - return [[CommandButton alloc] initWithIdentifier:identifier - title:@"Solo" - widget:findWidget_App("tracker") - command:@"tracker.solo"]; - } - else if ([identifier isEqualToString:color_TouchId_]) { - NSTouchBar *colors = [[NSTouchBar alloc] init]; - colors.delegate = self; - colors.defaultItemIdentifiers = @[ NSTouchBarItemIdentifierFlexibleSpace, - whiteColor_TouchId_, - yellowColor_TouchId_, - orangeColor_TouchId_, - redColor_TouchId_, - magentaColor_TouchId_, - blueColor_TouchId_, - cyanColor_TouchId_, - greenColor_TouchId_, - NSTouchBarItemIdentifierFlexibleSpace ]; - NSPopoverTouchBarItem *pop = [[NSPopoverTouchBarItem alloc] initWithIdentifier:identifier]; - pop.collapsedRepresentationImage = [NSImage imageNamed:NSImageNameTouchBarColorPickerFill]; - pop.popoverTouchBar = colors; - [colors release]; - return pop; - } - else if ([identifier isEqualToString:whiteColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:white_TrackColor]; - } - else if ([identifier isEqualToString:yellowColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:yellow_TrackColor]; - } - else if ([identifier isEqualToString:orangeColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:orange_TrackColor]; - } - else if ([identifier isEqualToString:redColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:red_TrackColor]; - } - else if ([identifier isEqualToString:magentaColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:magenta_TrackColor]; - } - else if ([identifier isEqualToString:blueColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:blue_TrackColor]; - } - else if ([identifier isEqualToString:cyanColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:cyan_TrackColor]; - } - else if ([identifier isEqualToString:greenColor_TouchId_]) { - return [[ColorButton alloc] initWithIdentifier:identifier - trackColor:green_TrackColor]; - } -#endif - return nil; -} - -@end - -void enableMomentumScroll_MacOS(void) { - [[NSUserDefaults standardUserDefaults] setBool: YES - forKey: @"AppleMomentumScrollSupported"]; -} - -void setupApplication_MacOS(void) { - NSApplication *app = [NSApplication sharedApplication]; - //appearanceChanged_MacOS_([[app effectiveAppearance] name]); - /* Our delegate will override SDL's delegate. */ - MyDelegate *myDel = [[MyDelegate alloc] initWithSDLDelegate:app.delegate]; - [myDel setAppearance:[[app effectiveAppearance] name]]; - app.delegate = myDel; - NSMenu *appMenu = [[[NSApp mainMenu] itemAtIndex:0] submenu]; - NSMenuItem *prefsItem = [appMenu itemWithTitle:@"Preferences…"]; - prefsItem.target = myDel; - prefsItem.action = @selector(showPreferences); - /* Get rid of the default window close item */ - NSMenu *windowMenu = [[[NSApp mainMenu] itemWithTitle:@"Window"] submenu]; - NSMenuItem *windowCloseItem = [windowMenu itemWithTitle:@"Close"]; - windowCloseItem.target = myDel; - windowCloseItem.action = @selector(closeTab); -} - -void insertMenuItems_MacOS(const char *menuLabel, int atIndex, const iMenuItem *items, size_t count) { - NSApplication *app = [NSApplication sharedApplication]; - MyDelegate *myDel = (MyDelegate *) app.delegate; - [app addObserver:myDel - forKeyPath:@"effectiveAppearance" - options:0 - context:myDel]; - NSMenu *appMenu = [app mainMenu]; - NSMenuItem *mainItem = [appMenu insertItemWithTitle:[NSString stringWithUTF8String:menuLabel] - action:nil - keyEquivalent:@"" - atIndex:atIndex]; - NSMenu *menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:menuLabel]]; - for (size_t i = 0; i < count; ++i) { - const char *label = items[i].label; - if (label[0] == '\r') { - /* Skip the formatting escape. */ - label += 2; - } - if (equal_CStr(label, "---")) { - [menu addItem:[NSMenuItem separatorItem]]; - } - else { - const iBool hasCommand = (items[i].command && items[i].command[0]); - iString key; - init_String(&key); - if (items[i].key == SDLK_LEFT) { - appendChar_String(&key, 0x2190); - } - else if (items[i].key == SDLK_RIGHT) { - appendChar_String(&key, 0x2192); - } - else if (items[i].key) { - appendChar_String(&key, items[i].key); - } - NSMenuItem *item = [menu addItemWithTitle:[NSString stringWithUTF8String:label] - action:(hasCommand ? @selector(postMenuItemCommand:) : nil) - keyEquivalent:[NSString stringWithUTF8String:cstr_String(&key)]]; - NSEventModifierFlags modMask = 0; - if (items[i].kmods & KMOD_GUI) { - modMask |= NSEventModifierFlagCommand; - } - if (items[i].kmods & KMOD_ALT) { - modMask |= NSEventModifierFlagOption; - } - if (items[i].kmods & KMOD_CTRL) { - modMask |= NSEventModifierFlagControl; - } - if (items[i].kmods & KMOD_SHIFT) { - modMask |= NSEventModifierFlagShift; - } - [item setKeyEquivalentModifierMask:modMask]; - if (hasCommand) { - [myDel setCommand:[NSString stringWithUTF8String:items[i].command] forMenuItem:item]; - } - deinit_String(&key); - } - } - [mainItem setSubmenu:menu]; - [menu release]; -} - -void handleCommand_MacOS(const char *cmd) { - if (equal_Command(cmd, "prefs.ostheme.changed")) { - if (arg_Command(cmd)) { - appearanceChanged_MacOS_([[NSApp effectiveAppearance] name]); - } - } -#if 0 - if (equal_Command(cmd, "tabs.changed")) { - MyDelegate *myDel = (MyDelegate *) [[NSApplication sharedApplication] delegate]; - const char *tabId = suffixPtr_Command(cmd, "id"); - if (equal_CStr(tabId, "tracker")) { - [myDel setTouchBarVariant:tracker_TouchBarVariant]; - } - else if (equal_CStr(tabId, "sequence")) { - [myDel setTouchBarVariant:sequence_TouchBarVariant]; - } - else if (equal_CStr(tabId, "trackertab")) { - [myDel setTouchBarVariant:wide_TouchBarVariant]; - } - else { - [myDel setTouchBarVariant:default_TouchBarVariant]; - } - } -#endif -} -- cgit v1.2.3