From 6160457965973035a83292b4a3076f633cfb285e Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 14 Jan 2022 19:49:12 +0100 Subject: [PATCH 1/5] Respect selection color in monitor widget We should have a consistent color set over all widgets. --- vncviewer/fltk/Fl_Monitor_Arrangement.cxx | 5 ++--- vncviewer/fltk/Fl_Monitor_Arrangement.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/vncviewer/fltk/Fl_Monitor_Arrangement.cxx b/vncviewer/fltk/Fl_Monitor_Arrangement.cxx index 3dddd8019c..7c72ff68f2 100644 --- a/vncviewer/fltk/Fl_Monitor_Arrangement.cxx +++ b/vncviewer/fltk/Fl_Monitor_Arrangement.cxx @@ -61,7 +61,6 @@ static const Fl_Boxtype FL_CHECKERED_BOX = FL_FREE_BOXTYPE; Fl_Monitor_Arrangement::Fl_Monitor_Arrangement( int x, int y, int w, int h) : Fl_Group(x, y, w, h), - SELECTION_COLOR(fl_lighter(FL_BLUE)), AVAILABLE_COLOR(fl_lighter(fl_lighter(fl_lighter(FL_BACKGROUND_COLOR)))) { // Used for required monitors. @@ -124,11 +123,11 @@ void Fl_Monitor_Arrangement::draw() if (is_required(iter->first)) { monitor->box(FL_CHECKERED_BOX); - monitor->color(SELECTION_COLOR); + monitor->color(FL_SELECTION_COLOR); } else { monitor->box(FL_BORDER_BOX); monitor->color(AVAILABLE_COLOR); - monitor->selection_color(SELECTION_COLOR); + monitor->selection_color(FL_SELECTION_COLOR); } } diff --git a/vncviewer/fltk/Fl_Monitor_Arrangement.h b/vncviewer/fltk/Fl_Monitor_Arrangement.h index b8e9ba7824..c71dd199d5 100644 --- a/vncviewer/fltk/Fl_Monitor_Arrangement.h +++ b/vncviewer/fltk/Fl_Monitor_Arrangement.h @@ -48,7 +48,6 @@ class Fl_Monitor_Arrangement: public Fl_Group { virtual void draw(); private: - const Fl_Color SELECTION_COLOR; const Fl_Color AVAILABLE_COLOR; typedef std::map MonitorMap; MonitorMap monitors; From 8f461b2d8942ee9a096835b25eb475a8d9d57b71 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 14 Jul 2023 10:06:49 +0200 Subject: [PATCH 2/5] Fix correct background in Fl_Input_Choice Work around a bug in Fl_Input_Choice where it forgets to set the proper "input background" on some parts. --- vncviewer/ServerDialog.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vncviewer/ServerDialog.cxx b/vncviewer/ServerDialog.cxx index 144f31a7fe..6a76629509 100644 --- a/vncviewer/ServerDialog.cxx +++ b/vncviewer/ServerDialog.cxx @@ -66,6 +66,8 @@ ServerDialog::ServerDialog() serverName = new Fl_Input_Choice(LBLLEFT(x, y, w() - OUTER_MARGIN*2, INPUT_HEIGHT, _("VNC server:"))); + // Bug fix for wrong background + serverName->color(FL_BACKGROUND2_COLOR); y += INPUT_HEIGHT + INNER_MARGIN; x2 = x; From 8109d90c1b917308fb725e132c181a898a026700 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 14 Jul 2023 10:08:25 +0200 Subject: [PATCH 3/5] Apply custom theme to FLTK Inspired by modern Windows appearance, and to some extent macOS. They have flat boxes and use white, or very light, colors for interactive elements. Unfortunately we can't directly control the colors of widgets, so instead we just lighten everything that uses this box type. GNOME uses a different design, both their older and newer style. But UI look is less consistent on Linux, so hopefully our new look is decent enough there as well. --- vncviewer/fltk/theme.cxx | 193 +++++++++++++++++++++++++++++++++++++-- vncviewer/fltk/theme.h | 18 +++- 2 files changed, 204 insertions(+), 7 deletions(-) diff --git a/vncviewer/fltk/theme.cxx b/vncviewer/fltk/theme.cxx index a5c0764ef7..1a6345b281 100644 --- a/vncviewer/fltk/theme.cxx +++ b/vncviewer/fltk/theme.cxx @@ -36,9 +36,161 @@ #include #include #include +#include #include "theme.h" +const int RADIUS = 4; + +static Fl_Color light_border(Fl_Color c) +{ + return fl_color_average(FL_BLACK, c, 0.12); +} + +static Fl_Color dark_border(Fl_Color c) +{ + return fl_color_average(FL_BLACK, c, 0.33); +} + +static void theme_round_frame(bool up, int x, int y, int w, int h, + int r, Fl_Color c) +{ + if (r > h/2) + r = h/2; + + // Top + if (up) + Fl::set_box_color(light_border(c)); + else + Fl::set_box_color(dark_border(c)); + + fl_xyline(x+r, y, x+w-r-1); + + // Top corners + if (!up) + Fl::set_box_color(fl_color_average(light_border(c), dark_border(c), 0.5)); + + fl_arc(x, y, r*2, r*2, 90.0, 180.0); + fl_arc(x+w-(r*2), y, r*2, r*2, 0, 90.0); + + // Left and right + Fl::set_box_color(light_border(c)); + + fl_yxline(x, y+r, y+h-r-1); + fl_yxline(x+w-1, y+r, y+h-r-1); + + // Bottom corners + if (up) + Fl::set_box_color(fl_color_average(light_border(c), dark_border(c), 0.5)); + + fl_arc(x, y+h-(r*2), r*2, r*2, 180, 270.0); + fl_arc(x+w-(r*2), y+h-(r*2), r*2, r*2, 270, 360.0); + + // Bottom + if (up) + Fl::set_box_color(dark_border(c)); + else + Fl::set_box_color(light_border(c)); + + fl_xyline(x+r, y+h-1, x+w-r-1); +} + +static void theme_up_frame(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_frame(true, x, y, w, h, RADIUS, c); +} + +static void theme_down_frame(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_frame(false, x, y, w, h, RADIUS, c); +} + +static void theme_round_rect(int x, int y, int w, int h, int r, + Fl_Color c, Fl_Color c2=-1) +{ + if (r > h/2) + r = h/2; + + if (c2 == (Fl_Color)-1) + c2 = c; + + // Top + Fl::set_box_color(c); + + fl_pie(x, y, r*2, r*2, 90.0, 180.0); + fl_rectf(x+r, y, w-(r*2), r); + fl_pie(x+w-(r*2), y, r*2, r*2, 0, 90.0); + + // Middle + const int steps = h - r*2; + for (int i = 0; i < steps; i++) { + Fl::set_box_color(fl_color_average(c2, c, (double)i/steps)); + fl_xyline(x, y+r+i, x+w-1); + } + + // Bottom + Fl::set_box_color(c2); + + fl_pie(x, y+h-(r*2), r*2, r*2, 180, 270.0); + fl_rectf(x+r, y+h-r, w-(r*2), r); + fl_pie(x+w-(r*2), y+h-(r*2), r*2, r*2, 270, 360.0); +} + +static void theme_up_box(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_rect(x, y, w, h, RADIUS, + fl_color_average(FL_WHITE, c, 0.75)); + theme_up_frame(x, y, w, h, c); +} + +static void theme_down_box(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_rect(x, y, w, h, RADIUS, c); + theme_down_frame(x, y, w, h, c); +} + +static void theme_thin_up_box(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_rect(x, y, w, h, RADIUS, c); + theme_up_frame(x, y, w, h, c); +} + +static void theme_thin_down_box(int x, int y, int w, int h, Fl_Color c) +{ + theme_round_rect(x, y, w, h, RADIUS, c); + theme_down_frame(x, y, w, h, c); +} + +static void theme_round_up_box(int x, int y, int w, int h, Fl_Color c) +{ + // Background + Fl::set_box_color(c); + fl_pie(x, y, w, h, 0.0, 360.0); + + // Outline + Fl::set_box_color(light_border(c)); + fl_arc(x, y, w, h, 0.0, 180.0); + Fl::set_box_color(dark_border(c)); + fl_arc(x, y, w, h, 180.0, 360.0); + Fl::set_box_color(fl_color_average(light_border(c), dark_border(c), 0.5)); + fl_arc(x, y, w, h, 225.0, 315.0); +} + +static void theme_round_down_box(int x, int y, int w, int h, Fl_Color c) +{ + // Background + Fl::set_box_color(c); + fl_pie(x, y, w, h, 0.0, 360.0); + + // Outline + Fl::set_box_color(fl_color_average(light_border(c), dark_border(c), 0.5)); + fl_arc(x, y, w, h, 0.0, 180.0); + Fl::set_box_color(dark_border(c)); + fl_arc(x, y, w, h, 45.0, 135.0); + Fl::set_box_color(light_border(c)); + fl_arc(x, y, w, h, 180.0, 360.0); +} + void init_theme() { #if defined(WIN32) || defined(__APPLE__) @@ -48,9 +200,8 @@ void init_theme() // Basic text size (10pt @ 96 dpi => 13px) FL_NORMAL_SIZE = 13; - // Select a FLTK scheme and background color that looks somewhat - // close to modern systems - Fl::scheme("gtk+"); + // Select a background color that looks somewhat close to modern + // systems Fl::background(220, 220, 220); // macOS has a slightly brighter default background though @@ -58,9 +209,39 @@ void init_theme() Fl::background(240, 240, 240); #endif - // This makes the "icon" in dialogs rounded, which fits better - // with the above schemes. - fl_message_icon()->box(FL_UP_BOX); + // We will override the box types later, but changing scheme affects + // more things than just those, so we still want to switch from the + // default + Fl::scheme("gtk+"); + + // Define our box types + const int PX = 2; + const int PY = 2; + + Fl::set_boxtype(THEME_UP_FRAME, theme_up_frame, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_DOWN_FRAME, theme_down_frame, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_THIN_UP_FRAME, theme_up_frame, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_THIN_DOWN_FRAME, theme_down_frame, PX, PY, PX*2, PY*2); + + Fl::set_boxtype(THEME_UP_BOX, theme_up_box, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_DOWN_BOX, theme_down_box, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_THIN_UP_BOX, theme_thin_up_box, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_THIN_DOWN_BOX, theme_thin_down_box, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_ROUND_UP_BOX, theme_round_up_box, PX, PY, PX*2, PY*2); + Fl::set_boxtype(THEME_ROUND_DOWN_BOX, theme_round_down_box, PX, PY, PX*2, PY*2); + + // Make them the default + Fl::set_boxtype(FL_UP_FRAME, THEME_UP_FRAME); + Fl::set_boxtype(FL_DOWN_FRAME, THEME_UP_FRAME); + Fl::set_boxtype(FL_THIN_UP_FRAME, THEME_THIN_UP_FRAME); + Fl::set_boxtype(FL_THIN_DOWN_FRAME, THEME_THIN_DOWN_FRAME); + + Fl::set_boxtype(FL_UP_BOX, THEME_UP_BOX); + Fl::set_boxtype(FL_DOWN_BOX, THEME_DOWN_BOX); + Fl::set_boxtype(FL_THIN_UP_BOX, THEME_THIN_UP_BOX); + Fl::set_boxtype(FL_THIN_DOWN_BOX, THEME_THIN_DOWN_BOX); + Fl::set_boxtype(_FL_ROUND_UP_BOX, THEME_ROUND_UP_BOX); + Fl::set_boxtype(_FL_ROUND_DOWN_BOX, THEME_ROUND_DOWN_BOX); #if defined(WIN32) NONCLIENTMETRICS metrics; diff --git a/vncviewer/fltk/theme.h b/vncviewer/fltk/theme.h index aa4b358630..8793bd7cd4 100644 --- a/vncviewer/fltk/theme.h +++ b/vncviewer/fltk/theme.h @@ -1,4 +1,4 @@ -/* Copyright 2022 Pierre Ossman for Cendio AB +/* Copyright 2023 Pierre Ossman for Cendio AB * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,6 +24,22 @@ #ifndef __FLTK_THEME_H__ #define __FLTK_THEME_H__ +#include + +#define _THEME_BOX_BASE (FL_FREE_BOXTYPE+1000) + +#define THEME_UP_FRAME (Fl_Boxtype)(_THEME_BOX_BASE+0) +#define THEME_DOWN_FRAME (Fl_Boxtype)(_THEME_BOX_BASE+1) +#define THEME_THIN_UP_FRAME (Fl_Boxtype)(_THEME_BOX_BASE+2) +#define THEME_THIN_DOWN_FRAME (Fl_Boxtype)(_THEME_BOX_BASE+3) + +#define THEME_UP_BOX (Fl_Boxtype)(_THEME_BOX_BASE+4) +#define THEME_DOWN_BOX (Fl_Boxtype)(_THEME_BOX_BASE+5) +#define THEME_THIN_UP_BOX (Fl_Boxtype)(_THEME_BOX_BASE+6) +#define THEME_THIN_DOWN_BOX (Fl_Boxtype)(_THEME_BOX_BASE+7) +#define THEME_ROUND_UP_BOX (Fl_Boxtype)(_THEME_BOX_BASE+8) +#define THEME_ROUND_DOWN_BOX (Fl_Boxtype)(_THEME_BOX_BASE+9) + void init_theme(); #endif From efbe273c11e0b236f03e095c7701172148d909e8 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 14 Jul 2023 13:10:27 +0200 Subject: [PATCH 4/5] Workaround for broken fl_arc()/fl_pie() There is something broken with these FLTK draw routines on Windows. They leave gaps at the start and end of the arc/pie rather than filling the whole specified span. So we need to nudge the numbers a bit to work around this. --- vncviewer/fltk/theme.cxx | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/vncviewer/fltk/theme.cxx b/vncviewer/fltk/theme.cxx index 1a6345b281..2c45c7341c 100644 --- a/vncviewer/fltk/theme.cxx +++ b/vncviewer/fltk/theme.cxx @@ -42,6 +42,31 @@ const int RADIUS = 4; +/* + * fl_arc() and fl_pie() are broken on Windows, so we need to fudge the + * numbers a bit + */ +#ifdef WIN32 +static void fixed_arc(int x,int y,int w,int h,double a1,double a2) { + if ((a1 != 0.0) || (a2 != 360.0)) { + a1 -= 10.0; + a2 += 10.0; + } + fl_arc(x, y, w, h, a1, a2); +} + +static void fixed_pie(int x,int y,int w,int h,double a1,double a2) { + if ((a1 != 0.0) || (a2 != 360.0)) { + a1 -= 10.0; + a2 += 10.0; + } + fl_pie(x, y, w, h, a1, a2); +} + +#define fl_arc fixed_arc +#define fl_pie fixed_pie +#endif + static Fl_Color light_border(Fl_Color c) { return fl_color_average(FL_BLACK, c, 0.12); From 723715cb10cc66d0e3343c3bbe3f3d3cb6f15b34 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 14 Jan 2022 19:51:47 +0100 Subject: [PATCH 5/5] Use modern UI colors Follow the colors that modern desktops use when it comes to background, text and selections. --- vncviewer/fltk/theme.cxx | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/vncviewer/fltk/theme.cxx b/vncviewer/fltk/theme.cxx index 2c45c7341c..f89933a315 100644 --- a/vncviewer/fltk/theme.cxx +++ b/vncviewer/fltk/theme.cxx @@ -225,15 +225,43 @@ void init_theme() // Basic text size (10pt @ 96 dpi => 13px) FL_NORMAL_SIZE = 13; - // Select a background color that looks somewhat close to modern - // systems - Fl::background(220, 220, 220); + // Modern colors based on the default appearance of Windows 11, + // macOS 12 and GNOME 41 - // macOS has a slightly brighter default background though -#ifdef __APPLE__ - Fl::background(240, 240, 240); + // FIXME: Should get these from the system, + // Fl::get_system_colors() is unfortunately not very capable + // FIXME: Should also handle dark mode + +#if defined(WIN32) + // Windows 11 + Fl::foreground(26, 26, 26); + Fl::background(243, 243, 243); +#elif defined(__APPLE__) + // FIXME: Text is rendered slightly lighter than what we specify here + // for some odd reason. The target is (38, 38, 38). + Fl::foreground(28, 28, 28); + Fl::background(246, 246, 246); +#else + // GNOME + Fl::foreground(46, 52, 54); + Fl::background(246, 245, 244); #endif +#if defined(WIN32) + // Windows 11 default accent color + Fl::set_color(FL_SELECTION_COLOR, 0, 103, 192); +#elif defined(__APPLE__) + Fl::set_color(FL_SELECTION_COLOR, 0, 122, 255); +#else + // GNOME + Fl::set_color(FL_SELECTION_COLOR, 53, 132, 228); +#endif + + // The arrow on Fl_Return_Button gets a invisible, so let's adjust it + // to compensate for our lighter buttons + Fl::set_color(FL_LIGHT3, light_border(fl_color_average(FL_WHITE, FL_BACKGROUND_COLOR, 0.5))); + Fl::set_color(FL_DARK3, dark_border(fl_color_average(FL_WHITE, FL_BACKGROUND_COLOR, 0.5))); + // We will override the box types later, but changing scheme affects // more things than just those, so we still want to switch from the // default