Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dark mode #1846

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions vncviewer/OptionsDialog.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
#include "parameters.h"

#include "fltk/layout.h"
#include "fltk/theme.h"
#include "fltk/util.h"
#include "fltk/Fl_Monitor_Arrangement.h"
#include "fltk/Fl_Navigation.h"
Expand Down Expand Up @@ -353,6 +354,15 @@ void OptionsDialog::loadOptions(void)
sharedCheckbox->value(shared);
reconnectCheckbox->value(reconnectOnError);
dotCursorCheckbox->value(dotWhenNoCursor);

/* Theme */
if (!strcasecmp(theme, "light")) {
lightThemeButton->setonly();
} else if (!strcasecmp(theme, "dark")) {
darkThemeButton->setonly();
} else {
autoThemeButton->setonly();
}
}


Expand Down Expand Up @@ -488,6 +498,15 @@ void OptionsDialog::storeOptions(void)
reconnectOnError.setParam(reconnectCheckbox->value());
dotWhenNoCursor.setParam(dotCursorCheckbox->value());

/* Theme */
if (lightThemeButton->value()) {
theme.setParam("Light");
} else if (darkThemeButton->value()) {
theme.setParam("Dark");
} else {
theme.setParam("Auto");
}

std::map<OptionsCallback*, void*>::const_iterator iter;

for (iter = callbacks.begin();iter != callbacks.end();++iter)
Expand Down Expand Up @@ -1028,9 +1047,16 @@ void OptionsDialog::createMiscPage(int tx, int ty, int tw, int th)
{
Fl_Group *group = new Fl_Group(tx, ty, tw, th, _("Miscellaneous"));

int orig_tx;
int width;

tx += OUTER_MARGIN;
ty += OUTER_MARGIN;

width = tw - OUTER_MARGIN * 2;

orig_tx = tx;

sharedCheckbox = new Fl_Check_Button(LBLRIGHT(tx, ty,
CHECK_MIN_WIDTH,
CHECK_HEIGHT,
Expand All @@ -1043,6 +1069,53 @@ void OptionsDialog::createMiscPage(int tx, int ty, int tw, int th)
_("Ask to reconnect on connection errors")));
ty += CHECK_HEIGHT + TIGHT_MARGIN;

/* Theme */
ty += GROUP_LABEL_OFFSET;
themeGroup = new Fl_Group(tx, ty, width, 0, _("Theme"));
themeGroup->labelfont(FL_BOLD);
themeGroup->box(FL_FLAT_BOX);
themeGroup->align(FL_ALIGN_LEFT | FL_ALIGN_TOP);

{
tx += INDENT;
ty += TIGHT_MARGIN;
width -= INDENT;

lightThemeButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
RADIO_MIN_WIDTH,
RADIO_HEIGHT,
_("Light")));
lightThemeButton->type(FL_RADIO_BUTTON);
lightThemeButton->callback(handleTheme, this);
ty += RADIO_HEIGHT + TIGHT_MARGIN;

darkThemeButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
RADIO_MIN_WIDTH,
RADIO_HEIGHT,
_("Dark")));
darkThemeButton->type(FL_RADIO_BUTTON);
darkThemeButton->callback(handleTheme, this);
ty += RADIO_HEIGHT + TIGHT_MARGIN;

autoThemeButton = new Fl_Round_Button(LBLRIGHT(tx, ty,
RADIO_MIN_WIDTH,
RADIO_HEIGHT,
_("Auto")));
autoThemeButton->type(FL_RADIO_BUTTON);
autoThemeButton->callback(handleTheme, this);
ty += RADIO_HEIGHT + TIGHT_MARGIN;
}
ty -= TIGHT_MARGIN;

themeGroup->end();
/* Needed for resize to work sanely */
themeGroup->resizable(nullptr);
themeGroup->size(themeGroup->w(), ty - themeGroup->y());

/* Back to normal */
tx = orig_tx;
ty += INNER_MARGIN;

group->end();
}

Expand Down Expand Up @@ -1140,6 +1213,24 @@ void OptionsDialog::handleFullScreenMode(Fl_Widget* /*widget*/, void *data)
}
}

void OptionsDialog::handleTheme(Fl_Widget* /*widget*/, void *data)
{
OptionsDialog *dialog = (OptionsDialog*)data;

// Update FLTK theme
if (dialog->lightThemeButton->value()) {
init_theme("Light");
} else if (dialog->darkThemeButton->value()) {
init_theme("Dark");
} else {
init_theme("Auto");
}

// Redraw all windows using new theme
for (Fl_Window* wnd = Fl::first_window(); wnd; wnd = Fl::next_window(wnd))
wnd->redraw();
}

void OptionsDialog::handleCancel(Fl_Widget* /*widget*/, void *data)
{
OptionsDialog *dialog = (OptionsDialog*)data;
Expand Down
6 changes: 6 additions & 0 deletions vncviewer/OptionsDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ class OptionsDialog : public Fl_Window {

static void handleFullScreenMode(Fl_Widget *widget, void *data);

static void handleTheme(Fl_Widget *widget, void *data);

static void handleCancel(Fl_Widget *widget, void *data);
static void handleOK(Fl_Widget *widget, void *data);

Expand Down Expand Up @@ -140,6 +142,10 @@ class OptionsDialog : public Fl_Window {
/* Misc. */
Fl_Check_Button *sharedCheckbox;
Fl_Check_Button *reconnectCheckbox;
Fl_Group *themeGroup;
Fl_Round_Button *lightThemeButton;
Fl_Round_Button *darkThemeButton;
Fl_Round_Button *autoThemeButton;

private:
static int fltk_event_handler(int event);
Expand Down
2 changes: 1 addition & 1 deletion vncviewer/UserDialog.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void UserDialog::getUserPasswd(bool secure_, std::string* user,
icon->box(FL_UP_BOX);
icon->labelfont(FL_TIMES_BOLD);
icon->labelsize(34);
icon->color(FL_WHITE);
icon->color(FL_BACKGROUND2_COLOR);
icon->labelcolor(FL_BLUE);

x += icon->w() + INNER_MARGIN;
Expand Down
87 changes: 72 additions & 15 deletions vncviewer/fltk/theme.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "theme.h"

const int RADIUS = 4;
static bool dark_mode = false;

/*
* fl_arc() and fl_pie() are broken on Windows, so we need to fudge the
Expand Down Expand Up @@ -165,8 +166,13 @@ static void theme_round_rect(int x, int y, int w, int h, int r,

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));
if (dark_mode) {
theme_round_rect(x, y, w, h, RADIUS,
fl_color_average(FL_WHITE, c, 0.1));
} else {
theme_round_rect(x, y, w, h, RADIUS,
fl_color_average(FL_WHITE, c, 0.75));
}
theme_up_frame(x, y, w, h, c);
}

Expand Down Expand Up @@ -218,8 +224,22 @@ static void theme_round_down_box(int x, int y, int w, int h, Fl_Color c)
fl_arc(x, y, w, h, 180.0, 360.0);
}

void init_theme()
static void set_theme(const char* theme)
{
if (!strcasecmp(theme, "light")) {
dark_mode = false;
} else if (!strcasecmp(theme, "dark")) {
dark_mode = true;
} else {
// FIXME: Determine use of dark mode from the system
dark_mode = false;
}
}

void init_theme(const char* theme)
{
set_theme(theme);

#if defined(WIN32) || defined(__APPLE__)
static char font_name[256];
#endif
Expand All @@ -232,37 +252,74 @@ void init_theme()

// 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);
if (dark_mode) {
Fl::foreground(255, 255, 255);
Fl::background(25, 25, 25);
Fl::background2(32, 32, 32);
} else {
Fl::foreground(26, 26, 26);
Fl::background(243, 243, 243);
Fl::background2(255, 255, 255);
}
#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);
if (dark_mode) {
Fl::foreground(223, 223, 223);
Fl::background(50, 50, 50);
Fl::background2(23, 23, 23);
} else {
Fl::foreground(28, 28, 28);
Fl::background(246, 246, 246);
Fl::background2(255, 255, 255);
}
#else
// GNOME
Fl::foreground(46, 52, 54);
Fl::background(246, 245, 244);
if (dark_mode) {
Fl::foreground(255, 255, 255);
Fl::background(51, 51, 51);
Fl::background2(45, 45, 45);
} else {
Fl::foreground(46, 52, 54);
Fl::background(246, 245, 244);
Fl::background2(255, 255, 255);
}
#endif

#if defined(WIN32)
// Windows 11 default accent color
Fl::set_color(FL_SELECTION_COLOR, 0, 103, 192);
if (dark_mode) {
Fl::set_color(FL_SELECTION_COLOR, 0, 160, 250);
} else {
Fl::set_color(FL_SELECTION_COLOR, 0, 103, 192);
}
#elif defined(__APPLE__)
Fl::set_color(FL_SELECTION_COLOR, 0, 122, 255);
if (dark_mode) {
Fl::set_color(FL_SELECTION_COLOR, 0, 87, 207);
} else {
Fl::set_color(FL_SELECTION_COLOR, 0, 122, 255);
}
#else
// GNOME
Fl::set_color(FL_SELECTION_COLOR, 53, 132, 228);
if (dark_mode) {
Fl::set_color(FL_SELECTION_COLOR, 21, 83, 158);
} else {
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)));
if (dark_mode) {
Fl::set_color(FL_LIGHT3, light_border(fl_color_average(FL_WHITE, FL_BACKGROUND_COLOR, 0.75)));
Fl::set_color(FL_DARK3, dark_border(fl_color_average(FL_WHITE, FL_BACKGROUND_COLOR, 0.75)));
} else {
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
Expand Down
2 changes: 1 addition & 1 deletion vncviewer/fltk/theme.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@
#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();
void init_theme(const char* theme);

#endif
6 changes: 6 additions & 0 deletions vncviewer/parameters.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ BoolParameter remoteResize("RemoteResize",
"the size of the local client window changes. "
"(Does not work with all servers)", true);

StringParameter theme("Theme",
"UI theme. Should be either Light, Dark "
" or Auto", "Auto");

BoolParameter viewOnly("ViewOnly",
"Don't send any mouse or keyboard events to the server",
false);
Expand Down Expand Up @@ -198,6 +202,8 @@ static VoidParameter* parameterArray[] = {
&fullScreen,
&fullScreenMode,
&fullScreenSelectedMonitors,
/* Theme */
&theme,
/* Input */
&viewOnly,
&emulateMiddleButton,
Expand Down
2 changes: 2 additions & 0 deletions vncviewer/parameters.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ extern rfb::StringParameter desktopSize;
extern rfb::StringParameter geometry;
extern rfb::BoolParameter remoteResize;

extern rfb::StringParameter theme;

extern rfb::BoolParameter listenMode;

extern rfb::BoolParameter viewOnly;
Expand Down
2 changes: 1 addition & 1 deletion vncviewer/vncviewer.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ static const char* getlocaledir()
static void init_fltk()
{
// Adjust look of FLTK
init_theme();
init_theme(theme);

// Proper Gnome Shell integration requires that we set a sensible
// WM_CLASS for the window.
Expand Down
Loading