diff --git a/FL/Enumerations.H b/FL/Enumerations.H index a13aca07e..a1a8cb7d0 100644 --- a/FL/Enumerations.H +++ b/FL/Enumerations.H @@ -407,7 +407,11 @@ enum Fl_Event { // events /** A zoom event (ctrl/+/-/0/ or cmd/+/-/0/) was processed. Use Fl::add_handler() to be notified of this event. */ - FL_ZOOM_EVENT = 27 + FL_ZOOM_EVENT = 27, + /** The current dynamic color mode has changed. + Call Fl::dynamic_color() to get the new mode. + */ + FL_DYNAMIC_COLOR_CHANGED = 28 // DEV NOTE: Keep this list in sync with FL/names.h }; @@ -1380,4 +1384,26 @@ enum Fl_Orientation { FL_ORIENT_SE = 0x07 ///< GUI element pointing SE (315°) }; +/** + Modern operating systems provide a pair of color themes suitable for use in + light and dark ambient lighting. Automatic switching is normally provided + either using the current time of day or with ambient lighting sensors. + This enumeration lists the values for the Fl::dynamic_color() API, allowing + applications to opt in to automatic color theme switching or specify that + color switching is not desired. + + \see Fl::dynamic_color() + */ + +enum Fl_Dynamic_Color { // dynamic color values + /** Dynamic color themes are off/not supported/disabled */ + FL_DYNAMIC_COLOR_OFF, + /** Automatically change color themes */ + FL_DYNAMIC_COLOR_AUTO, + /** Use the light color theme */ + FL_DYNAMIC_COLOR_LIGHT, + /** Use the dark color theme */ + FL_DYNAMIC_COLOR_DARK +}; + #endif diff --git a/FL/Fl.H b/FL/Fl.H index 3757b65be..39a8e22bb 100644 --- a/FL/Fl.H +++ b/FL/Fl.H @@ -148,6 +148,8 @@ private: public: + static Fl_Dynamic_Color dynamic_color_; + static Fl_Screen_Driver *screen_driver(); static Fl_System_Driver *system_driver(); #ifdef __APPLE__ // deprecated in 1.4 - only for compatibility with 1.3 @@ -392,6 +394,27 @@ public: static void background(uchar, uchar, uchar); static void background2(uchar, uchar, uchar); + // dynamic color: + /** Set the dynamic color theme mode. + + Most applications should just call Fl::dynamic_color(FL_DYNAMIC_COLOR_AUTO) + to use the user's preferred color theme. The default value is + FL_DYNAMIC_COLOR_OFF for compatibility with prior versions of FLTK. + + When set to FL_DYNAMIC_COLOR_AUTO, FLTK will monitor for appearance/theme + changes. When the color theme changes, a FL_DYNAMIC_COLOR_CHANGED event is + sent to allow widgets to update their colors as needed. + */ + static void dynamic_color(Fl_Dynamic_Color mode); + /** Gets the current dynamic color theme. + + Returns FL_DYNAMIC_COLOR_OFF if dynamic color themes are off or disabled + (the default), FL_DYNAMIC_COLOR_LIGHT if the current color theme is for use + in bright ambient light, or FL_DYNAMIC_COLOR_DARK is the current color + theme is for use in dark ambient light. + */ + static Fl_Dynamic_Color dynamic_color(); + // schemes: static int scheme(const char *name); /** See void scheme(const char *name) */ diff --git a/FL/names.h b/FL/names.h index 15a491f84..ef1972b2e 100644 --- a/FL/names.h +++ b/FL/names.h @@ -73,7 +73,7 @@ const char * const fl_eventnames[] = "FL_FULLSCREEN", "FL_ZOOM_GESTURE", "FL_ZOOM_EVENT", - "FL_EVENT_28", // not yet defined, just in case it /will/ be defined ... + "FL_DYNAMIC_COLOR_CHANGED", "FL_EVENT_29", // not yet defined, just in case it /will/ be defined ... "FL_EVENT_30" // not yet defined, just in case it /will/ be defined ... }; diff --git a/src/Fl_System_Driver.H b/src/Fl_System_Driver.H index 3dad3d754..f5441f364 100644 --- a/src/Fl_System_Driver.H +++ b/src/Fl_System_Driver.H @@ -222,6 +222,7 @@ public: virtual const char *alt_name() { return "Alt"; } virtual const char *control_name() { return "Ctrl"; } virtual Fl_Sys_Menu_Bar_Driver *sys_menu_bar_driver() { return NULL; } + virtual Fl_Dynamic_Color dynamic_color() { return FL_DYNAMIC_COLOR_LIGHT; } virtual void lock_ring() {} virtual void unlock_ring() {} virtual double wait(double); // must FL_OVERRIDE diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 9e51a6efa..b5e11ce3a 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -580,6 +580,7 @@ void Fl_Cocoa_Screen_Driver::breakMacEventLoop() - (BOOL)process_keydown:(NSEvent*)theEvent; - (id)initWithFrame:(NSRect)frameRect; - (void)drawRect:(NSRect)rect; +- (void)layout; - (BOOL)acceptsFirstResponder; - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; - (void)resetCursorRects; @@ -2415,6 +2416,38 @@ static FLTextInputContext* fltextinputcontext_instance = nil; fl_unlock_function(); } +- (void)layout +{ + // Check to see whether the system appearance has changed... + if (Fl::dynamic_color_ == FL_DYNAMIC_COLOR_AUTO) { + Fl_Dynamic_Color mode = FL_DYNAMIC_COLOR_LIGHT; + + if (@available(macOS 10.14, *)) { + NSAppearanceName temp = [self.effectiveAppearance bestMatchFromAppearancesWithNames:@[ + NSAppearanceNameAqua, + NSAppearanceNameDarkAqua + ]]; + + if ([temp isEqualToString:NSAppearanceNameDarkAqua]) + mode = FL_DYNAMIC_COLOR_DARK; + } + + Fl_Darwin_System_Driver *s = (Fl_Darwin_System_Driver *)Fl::system_driver(); + if (s->dynamic_color() != mode) { + // Change the color mode and force all windows to redraw... + s->dynamic_color(mode); + Fl::get_system_colors(); + + Fl_Window *window; + for (window = Fl::first_window(); window; window = Fl::next_window(window)) + window->redraw(); + } + } + + [super layout]; +} + + - (BOOL)acceptsFirstResponder { return [[self window] parentWindow] ? NO : YES; // 10.2 diff --git a/src/Fl_get_system_colors.cxx b/src/Fl_get_system_colors.cxx index 6e9f5249e..0311c7ef1 100644 --- a/src/Fl_get_system_colors.cxx +++ b/src/Fl_get_system_colors.cxx @@ -106,6 +106,27 @@ void Fl::get_system_colors() Fl::screen_driver()->get_system_colors(); } +#ifndef FL_DOXYGEN +Fl_Dynamic_Color Fl::dynamic_color_ = FL_DYNAMIC_COLOR_OFF; +#endif // !FL_DOXYGEN + +void Fl::dynamic_color(Fl_Dynamic_Color mode) +{ + dynamic_color_ = mode; +} + +Fl_Dynamic_Color Fl::dynamic_color() +{ + // TODO: Remove this + if (getenv("FL_DYNAMIC_COLOR") && dynamic_color_ == FL_DYNAMIC_COLOR_OFF) + dynamic_color_ = FL_DYNAMIC_COLOR_AUTO; + + if (dynamic_color_ == FL_DYNAMIC_COLOR_AUTO) + return Fl::system_driver()->dynamic_color(); + else + return dynamic_color_; +} + //// Simple implementation of 2.0 Fl::scheme() interface... #define D1 BORDER_WIDTH diff --git a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx index 7eb98b291..39d3fa965 100644 --- a/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx +++ b/src/drivers/Cocoa/Fl_Cocoa_Screen_Driver.cxx @@ -201,7 +201,11 @@ static void set_selection_color(uchar r, uchar g, uchar b) } -// MacOS X currently supports two color schemes - Blue and Graphite. +// macOS supports two basic color schemes - Light and Dark - with accent and +// highlight colors. Older versions limited the accent colors to Blue and +// Graphite but now you can pick from a rainbow of colors plus the old Graphite +// gray. +// // Since we aren't emulating the Aqua interface (even if Apple would // let us), we use some defaults that are similar to both. The // Fl::scheme("plastic") color/box scheme provides a usable Aqua-like @@ -212,13 +216,30 @@ void Fl_Cocoa_Screen_Driver::get_system_colors() Fl_Screen_Driver::get_system_colors(); - if (!bg2_set) Fl::background2(0xff, 0xff, 0xff); - if (!fg_set) Fl::foreground(0, 0, 0); - if (!bg_set) Fl::background(0xd8, 0xd8, 0xd8); + Fl_Dynamic_Color mode = Fl::dynamic_color(); + + if (!bg2_set || mode != FL_DYNAMIC_COLOR_OFF) { + if (mode == FL_DYNAMIC_COLOR_DARK) + Fl::background2(23, 23, 23); + else + Fl::background2(0xff, 0xff, 0xff); + } + if (!fg_set || mode != FL_DYNAMIC_COLOR_OFF) { + if (mode == FL_DYNAMIC_COLOR_DARK) + Fl::foreground(223, 223, 223); + else + Fl::foreground(0, 0, 0); + } + if (!bg_set || mode != FL_DYNAMIC_COLOR_OFF) { + if (mode == FL_DYNAMIC_COLOR_DARK) + Fl::background(50, 50, 50); + else + Fl::background(0xd8, 0xd8, 0xd8); + } #if 0 // this would be the correct code, but it does not run on all versions - // of OS X. Also, setting a bright selection color would require + // of macOS. Also, setting a bright selection color would require // some updates in Fl_Adjuster and Fl_Help_Browser OSStatus err; RGBColor c; @@ -229,6 +250,7 @@ void Fl_Cocoa_Screen_Driver::get_system_colors() set_selection_color(c.red, c.green, c.blue); #else set_selection_color(0x00, 0x00, 0x80); + //set_selection_color(0, 87, 207); #endif } diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.H b/src/drivers/Darwin/Fl_Darwin_System_Driver.H index 776f68738..c0b9ece4b 100644 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.H +++ b/src/drivers/Darwin/Fl_Darwin_System_Driver.H @@ -40,6 +40,8 @@ class Fl_Darwin_System_Driver : public Fl_Posix_System_Driver { + Fl_Dynamic_Color dynamic_color_; + public: Fl_Darwin_System_Driver(); int single_arg(const char *arg) FL_OVERRIDE; @@ -82,6 +84,8 @@ public: Fl_Sys_Menu_Bar_Driver *sys_menu_bar_driver() FL_OVERRIDE; double wait(double time_to_wait) FL_OVERRIDE; int ready() FL_OVERRIDE; + Fl_Dynamic_Color dynamic_color() FL_OVERRIDE; + void dynamic_color(Fl_Dynamic_Color mode) { dynamic_color_ = mode; } }; #endif // FL_DARWIN_SYSTEM_DRIVER_H diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx b/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx index c44ba34c1..3d81c2d2f 100644 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx +++ b/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx @@ -51,8 +51,9 @@ const char *Fl_Darwin_System_Driver::control_name() { Fl_Darwin_System_Driver::Fl_Darwin_System_Driver() : Fl_Posix_System_Driver() { if (fl_mac_os_version == 0) fl_mac_os_version = calc_mac_os_version(); - command_key = FL_META; - control_key = FL_CTRL; + command_key = FL_META; + control_key = FL_CTRL; + dynamic_color_ = FL_DYNAMIC_COLOR_LIGHT; } int Fl_Darwin_System_Driver::single_arg(const char *arg) { @@ -404,3 +405,7 @@ Fl_Pixmap *Fl_Darwin_System_Driver::tree_closepixmap() { int Fl_Darwin_System_Driver::tree_connector_style() { return FL_TREE_CONNECTOR_NONE; } + +Fl_Dynamic_Color Fl_Darwin_System_Driver::dynamic_color() { + return dynamic_color_; +}