From b3918c8814e1d7869e0c9430812724671bfda3ec Mon Sep 17 00:00:00 2001 From: Philip Pitts <84428015+philippitts@users.noreply.github.com> Date: Thu, 29 Feb 2024 00:12:40 -0500 Subject: [PATCH 1/7] Fix Cyton impedance check board configuration --- OpenBCI_GUI/BoardCyton.pde | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenBCI_GUI/BoardCyton.pde b/OpenBCI_GUI/BoardCyton.pde index fdfaf46d0..71ae90f38 100644 --- a/OpenBCI_GUI/BoardCyton.pde +++ b/OpenBCI_GUI/BoardCyton.pde @@ -450,8 +450,8 @@ implements ImpedanceSettingsBoard, AccelerometerCapableBoard, AnalogCapableBoard currentADS1299Settings.values.gain[channel] = Gain.X1; currentADS1299Settings.values.inputType[channel] = InputType.NORMAL; - currentADS1299Settings.values.bias[channel] = Bias.INCLUDE; - currentADS1299Settings.values.srb2[channel] = Srb2.DISCONNECT; + currentADS1299Settings.values.bias[channel] = Bias.NO_INCLUDE; + currentADS1299Settings.values.srb2[channel] = Srb2.CONNECT; currentADS1299Settings.values.srb1[channel] = Srb1.DISCONNECT; fullCommand.append(currentADS1299Settings.getValuesString(channel, currentADS1299Settings.values)); From 552311e23c0edddfa92e5b4d6e1ecc11a4f61609 Mon Sep 17 00:00:00 2001 From: Richard Waltman Date: Mon, 12 Jan 2026 14:00:58 -0600 Subject: [PATCH 2/7] Update Wix package GUID --- release/wix/Package.wxs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/wix/Package.wxs b/release/wix/Package.wxs index f837f85b6..62a0e6c5f 100644 --- a/release/wix/Package.wxs +++ b/release/wix/Package.wxs @@ -6,7 +6,7 @@ Name='OpenBCI GUI' Manufacturer='OpenBCI, Inc.' Version='7.0.0' - UpgradeCode='75eade24-72b2-4019-9256-c8a3411e5874' + UpgradeCode='45d666b4-ab22-4d2c-9ba3-1d143812dbc0' Compressed='yes' > From 4ba712b5f7736e2af4d476c6737110204e155ea3 Mon Sep 17 00:00:00 2001 From: anaya yorke Date: Sat, 28 Feb 2026 02:22:37 -0500 Subject: [PATCH 3/7] Add dark mode theme support (Style.pde) - Issue #705 --- OpenBCI_GUI/ControlPanel.pde | 6 +- OpenBCI_GUI/Debugging.pde | 78 ++++---- OpenBCI_GUI/Interactivity.pde | 12 +- OpenBCI_GUI/OpenBCI_GUI.pde | 5 + OpenBCI_GUI/Style.pde | 326 ++++++++++++++++++++++++++++++++++ OpenBCI_GUI/TopNav.pde | 28 +-- OpenBCI_GUI/Widget.pde | 8 +- 7 files changed, 385 insertions(+), 78 deletions(-) create mode 100644 OpenBCI_GUI/Style.pde diff --git a/OpenBCI_GUI/ControlPanel.pde b/OpenBCI_GUI/ControlPanel.pde index e50e9ea1e..601191b3f 100644 --- a/OpenBCI_GUI/ControlPanel.pde +++ b/OpenBCI_GUI/ControlPanel.pde @@ -227,14 +227,14 @@ class ControlPanel { //draw the box that tells you to stop the system in order to edit control settings if (drawStopInstructions) { pushStyle(); - fill(boxColor); + fill(style.getBoxColor()); strokeWeight(1); - stroke(boxStrokeColor); + stroke(style.getBoxStrokeColor()); rect(x, y, w, dataSourceBox.h); //draw background of box String stopInstructions = "Press the \"STOP SESSION\" button to change your data source or edit system settings."; textAlign(CENTER, TOP); textFont(p4, 14); - fill(OPENBCI_DARKBLUE); + fill(style.getTextColor()); text(stopInstructions, x + globalPadding*2, y + globalPadding*3, w - globalPadding*4, dataSourceBox.h - globalPadding*4); popStyle(); } diff --git a/OpenBCI_GUI/Debugging.pde b/OpenBCI_GUI/Debugging.pde index ecf144322..1f67a9fe9 100644 --- a/OpenBCI_GUI/Debugging.pde +++ b/OpenBCI_GUI/Debugging.pde @@ -75,56 +75,48 @@ class HelpWidget { pushStyle(); - if (colorScheme == COLOR_SCHEME_DEFAULT) { - // draw background of widget - stroke(OPENBCI_DARKBLUE); - fill(255); - rect(-1, height-h, width+2, h); - noStroke(); - - //draw bg of text field of widget - strokeWeight(1); - stroke(color(0, 5, 11)); - fill(color(0, 5, 11)); - rect(x + padding, height-h + padding, width - padding*2, h - padding *2); - - textFont(p4); - textSize(14); - fill(255); - textAlign(LEFT, TOP); - text(currentOutput, padding*2, height - h + padding); - } else if (colorScheme == COLOR_SCHEME_ALTERNATIVE_A){ - // draw background of widget - stroke(OPENBCI_DARKBLUE); - fill(OPENBCI_BLUE); - rect(-1, height-h, width+2, h); - noStroke(); - - //draw bg of text field of widget - strokeWeight(1); - int saturationFadeValue = 0; - if (outputWasTriggered) { - int timeDelta = millis() - colorFadeCounter; - saturationFadeValue = (int)map(timeDelta, 0, colorFadeTimeMillis, 100, 0); - if (timeDelta > colorFadeTimeMillis) { - outputWasTriggered = false; - } + // Use the global style manager for theme-aware colors + color helpBg = style.getHelpWidgetBackground(); + color helpTextBg = style.getHelpWidgetTextBackground(); + color helpStroke = style.getBoxStrokeColor(); + + // draw background of widget + stroke(helpStroke); + fill(helpBg); + rect(-1, height-h, width+2, h); + noStroke(); + + //draw bg of text field of widget + strokeWeight(1); + int saturationFadeValue = 0; + if (outputWasTriggered) { + int timeDelta = millis() - colorFadeCounter; + saturationFadeValue = (int)map(timeDelta, 0, colorFadeTimeMillis, 100, 0); + if (timeDelta > colorFadeTimeMillis) { + outputWasTriggered = false; } + } + + // For dark mode, use solid colors; for other modes, use HSB fade effect + if (style.isDarkMode()) { + stroke(helpTextBg); + fill(helpTextBg); + } else { //Colors in this method are calculated using Hue, Saturation, Brightness colorMode(HSB, 360, 100, 100); color c = getBackgroundColor(saturationFadeValue); stroke(c); fill(c); - rect(x + padding, height-h + padding, width - padding*2, h - padding *2); - - // Revert color mode back to standard RGB here - colorMode(RGB, 255, 255, 255); - textFont(p4); - textSize(14); - fill(getTextColor()); - textAlign(LEFT, TOP); - text(currentOutput, padding*2, height - h + padding); } + rect(x + padding, height-h + padding, width - padding*2, h - padding *2); + + // Revert color mode back to standard RGB here + colorMode(RGB, 255, 255, 255); + textFont(p4); + textSize(14); + fill(style.isDarkMode() ? style.getTextColor() : getTextColor()); + textAlign(LEFT, TOP); + text(currentOutput, padding*2, height - h + padding); popStyle(); } diff --git a/OpenBCI_GUI/Interactivity.pde b/OpenBCI_GUI/Interactivity.pde index eba496f1c..045db0d0a 100644 --- a/OpenBCI_GUI/Interactivity.pde +++ b/OpenBCI_GUI/Interactivity.pde @@ -58,15 +58,9 @@ void parseKey(char val) { drawContainers = !drawContainers; return; case '{': - /* - if(colorScheme == COLOR_SCHEME_DEFAULT){ - colorScheme = COLOR_SCHEME_ALTERNATIVE_A; - } else if(colorScheme == COLOR_SCHEME_ALTERNATIVE_A) { - colorScheme = COLOR_SCHEME_DEFAULT; - } - */ - //topNav.updateNavButtonsBasedOnColorScheme(); - output("New Dark color scheme coming soon!"); + // Cycle through themes: Default -> Dark -> Light -> Default... + style.cycleTheme(); + output("Theme changed to: " + style.getThemeName()); return; //deactivate channels 1-4 diff --git a/OpenBCI_GUI/OpenBCI_GUI.pde b/OpenBCI_GUI/OpenBCI_GUI.pde index b6cb0cbc1..2c0228709 100644 --- a/OpenBCI_GUI/OpenBCI_GUI.pde +++ b/OpenBCI_GUI/OpenBCI_GUI.pde @@ -402,6 +402,11 @@ void setup() { directoryManager = new DirectoryManager(); + // Initialize the global style/theme manager + // This must be done early so all UI components can access theme colors + style = new Style(); + println("Style: Initialized with " + style.getThemeName() + " theme"); + // redirect all output to a custom stream that will intercept all prints // write them to file and display them in the GUI's console window outputStream = new CustomOutputStream(System.out); diff --git a/OpenBCI_GUI/Style.pde b/OpenBCI_GUI/Style.pde new file mode 100644 index 000000000..fd7179b48 --- /dev/null +++ b/OpenBCI_GUI/Style.pde @@ -0,0 +1,326 @@ +/////////////////////////////////////////////////////////////////////////////////////// +// +// Style.pde - Theme Manager for OpenBCI GUI +// +// Created for GUI v7 Dark Mode Feature (Issue #705) +// This class centralizes all color definitions and allows switching between themes. +// +// Themes available: +// - THEME_DEFAULT: Original OpenBCI blue theme +// - THEME_DARK: Dark mode for accessibility (reduced eye strain) +// - THEME_LIGHT: Light/white theme +// +/////////////////////////////////////////////////////////////////////////////////////// + +// Theme constants - these identify which theme is active +final int THEME_DEFAULT = 0; // Original OpenBCI blue +final int THEME_DARK = 1; // Dark mode +final int THEME_LIGHT = 2; // Light mode + +// Global style instance - use this throughout the GUI +Style style; + +/** + * Style class manages all GUI colors based on the selected theme. + * + * HOW TO USE: + * Instead of using hardcoded colors like: + * fill(color(200)); // old way + * + * Use the style methods: + * fill(style.getBoxColor()); // new way + * + * This allows colors to change automatically when the theme changes. + */ +class Style { + + // Current active theme + private int currentTheme; + + // ============================================================ + // COLOR DEFINITIONS FOR EACH THEME + // ============================================================ + + // --- THEME_DEFAULT (Original OpenBCI Blue) --- + private final color DEFAULT_TOPNAV_BG = color(31, 69, 110); // OPENBCI_BLUE + private final color DEFAULT_SUBNAV_BG = color(57, 128, 204); // buttonsLightBlue + private final color DEFAULT_BOX_BG = color(200); // boxColor + private final color DEFAULT_BOX_STROKE = color(1, 18, 41); // OPENBCI_DARKBLUE + private final color DEFAULT_WIDGET_BG = color(255); // WHITE + private final color DEFAULT_TEXT_PRIMARY = color(1, 18, 41); // OPENBCI_DARKBLUE + private final color DEFAULT_TEXT_SECONDARY = color(100); // GREY_100 + private final color DEFAULT_TEXT_ON_DARK = color(255); // WHITE + private final color DEFAULT_BUTTON_BG = color(57, 128, 204); // buttonsLightBlue + private final color DEFAULT_BUTTON_TEXT = color(255); // WHITE + private final color DEFAULT_BUTTON_HOVER = color(177, 184, 193); + private final color DEFAULT_BUTTON_PRESSED = color(150, 170, 200); + private final color DEFAULT_HELP_WIDGET_BG = color(31, 69, 110); // OPENBCI_BLUE + private final color DEFAULT_HELP_WIDGET_TEXT_BG = color(1, 18, 41); + + // --- THEME_DARK (Dark Mode) --- + private final color DARK_TOPNAV_BG = color(25, 25, 30); // Very dark blue-grey + private final color DARK_SUBNAV_BG = color(35, 35, 45); // Slightly lighter + private final color DARK_BOX_BG = color(45, 45, 55); // Dark grey for boxes + private final color DARK_BOX_STROKE = color(70, 70, 80); // Subtle border + private final color DARK_WIDGET_BG = color(30, 30, 38); // Dark widget background + private final color DARK_TEXT_PRIMARY = color(230, 230, 235); // Off-white text + private final color DARK_TEXT_SECONDARY = color(160, 160, 170); // Muted text + private final color DARK_TEXT_ON_DARK = color(230, 230, 235); // Same as primary in dark + private final color DARK_BUTTON_BG = color(60, 65, 80); // Dark button + private final color DARK_BUTTON_TEXT = color(230, 230, 235); // Light text on buttons + private final color DARK_BUTTON_HOVER = color(75, 80, 95); // Slightly lighter on hover + private final color DARK_BUTTON_PRESSED = color(50, 55, 70); // Darker when pressed + private final color DARK_HELP_WIDGET_BG = color(25, 25, 30); // Match topnav + private final color DARK_HELP_WIDGET_TEXT_BG = color(20, 20, 25); // Very dark + + // --- THEME_LIGHT (Light Mode) --- + private final color LIGHT_TOPNAV_BG = color(255); // White + private final color LIGHT_SUBNAV_BG = color(245, 245, 248); // Very light grey + private final color LIGHT_BOX_BG = color(250, 250, 252); // Almost white + private final color LIGHT_BOX_STROKE = color(200, 200, 205); // Light grey border + private final color LIGHT_WIDGET_BG = color(255); // White + private final color LIGHT_TEXT_PRIMARY = color(30, 30, 35); // Near black + private final color LIGHT_TEXT_SECONDARY = color(100, 100, 110); // Grey + private final color LIGHT_TEXT_ON_DARK = color(255); // White (for accent buttons) + private final color LIGHT_BUTTON_BG = color(31, 69, 110); // Keep OpenBCI blue for buttons + private final color LIGHT_BUTTON_TEXT = color(255); // White on blue buttons + private final color LIGHT_BUTTON_HOVER = color(45, 90, 140); // Lighter blue + private final color LIGHT_BUTTON_PRESSED = color(20, 50, 90); // Darker blue + private final color LIGHT_HELP_WIDGET_BG = color(245, 245, 248); // Light grey + private final color LIGHT_HELP_WIDGET_TEXT_BG = color(255); // White + + // ============================================================ + // CONSTRUCTOR + // ============================================================ + + Style() { + // Default to the original OpenBCI theme + this.currentTheme = THEME_DEFAULT; + } + + Style(int theme) { + this.currentTheme = theme; + } + + // ============================================================ + // THEME SWITCHING + // ============================================================ + + /** + * Set the current theme + * @param theme Use THEME_DEFAULT, THEME_DARK, or THEME_LIGHT + */ + void setTheme(int theme) { + if (theme >= THEME_DEFAULT && theme <= THEME_LIGHT) { + this.currentTheme = theme; + println("Style: Theme changed to " + getThemeName()); + } + } + + /** + * Get the current theme ID + */ + int getTheme() { + return this.currentTheme; + } + + /** + * Get the current theme name as a string + */ + String getThemeName() { + switch(currentTheme) { + case THEME_DARK: + return "Dark"; + case THEME_LIGHT: + return "Light"; + default: + return "Default"; + } + } + + /** + * Cycle to the next theme (useful for toggle buttons) + */ + void cycleTheme() { + currentTheme = (currentTheme + 1) % 3; + println("Style: Theme cycled to " + getThemeName()); + } + + /** + * Check if dark mode is active + */ + boolean isDarkMode() { + return currentTheme == THEME_DARK; + } + + // ============================================================ + // COLOR GETTER METHODS + // These return the appropriate color based on current theme + // ============================================================ + + // --- Navigation Colors --- + + color getTopNavBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_TOPNAV_BG; + case THEME_LIGHT: return LIGHT_TOPNAV_BG; + default: return DEFAULT_TOPNAV_BG; + } + } + + color getSubNavBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_SUBNAV_BG; + case THEME_LIGHT: return LIGHT_SUBNAV_BG; + default: return DEFAULT_SUBNAV_BG; + } + } + + // --- Box/Panel Colors --- + + color getBoxColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BOX_BG; + case THEME_LIGHT: return LIGHT_BOX_BG; + default: return DEFAULT_BOX_BG; + } + } + + color getBoxStrokeColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BOX_STROKE; + case THEME_LIGHT: return LIGHT_BOX_STROKE; + default: return DEFAULT_BOX_STROKE; + } + } + + // --- Widget Colors --- + + color getWidgetBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_WIDGET_BG; + case THEME_LIGHT: return LIGHT_WIDGET_BG; + default: return DEFAULT_WIDGET_BG; + } + } + + // --- Text Colors --- + + color getTextColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_TEXT_PRIMARY; + case THEME_LIGHT: return LIGHT_TEXT_PRIMARY; + default: return DEFAULT_TEXT_PRIMARY; + } + } + + color getSecondaryTextColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_TEXT_SECONDARY; + case THEME_LIGHT: return LIGHT_TEXT_SECONDARY; + default: return DEFAULT_TEXT_SECONDARY; + } + } + + color getTextOnDarkBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_TEXT_ON_DARK; + case THEME_LIGHT: return LIGHT_TEXT_ON_DARK; + default: return DEFAULT_TEXT_ON_DARK; + } + } + + // --- Button Colors --- + + color getButtonColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BUTTON_BG; + case THEME_LIGHT: return LIGHT_BUTTON_BG; + default: return DEFAULT_BUTTON_BG; + } + } + + color getButtonTextColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BUTTON_TEXT; + case THEME_LIGHT: return LIGHT_BUTTON_TEXT; + default: return DEFAULT_BUTTON_TEXT; + } + } + + color getButtonHoverColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BUTTON_HOVER; + case THEME_LIGHT: return LIGHT_BUTTON_HOVER; + default: return DEFAULT_BUTTON_HOVER; + } + } + + color getButtonPressedColor() { + switch(currentTheme) { + case THEME_DARK: return DARK_BUTTON_PRESSED; + case THEME_LIGHT: return LIGHT_BUTTON_PRESSED; + default: return DEFAULT_BUTTON_PRESSED; + } + } + + // --- Help Widget (Console) Colors --- + + color getHelpWidgetBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_HELP_WIDGET_BG; + case THEME_LIGHT: return LIGHT_HELP_WIDGET_BG; + default: return DEFAULT_HELP_WIDGET_BG; + } + } + + color getHelpWidgetTextBackground() { + switch(currentTheme) { + case THEME_DARK: return DARK_HELP_WIDGET_TEXT_BG; + case THEME_LIGHT: return LIGHT_HELP_WIDGET_TEXT_BG; + default: return DEFAULT_HELP_WIDGET_TEXT_BG; + } + } + + // --- Special Colors (these stay consistent across themes for recognition) --- + + color getSuccessColor() { + return color(195, 242, 181); // TURN_ON_GREEN - always green + } + + color getErrorColor() { + return color(224, 56, 45); // BOLD_RED - always red + } + + color getWarningColor() { + return color(221, 178, 13); // SIGNAL_CHECK_YELLOW - always yellow + } + + color getAccentColor() { + // OpenBCI blue accent - slightly adjusted for dark mode visibility + switch(currentTheme) { + case THEME_DARK: return color(70, 130, 200); // Brighter blue for dark bg + case THEME_LIGHT: return color(31, 69, 110); // Original OPENBCI_BLUE + default: return color(57, 128, 204); // buttonsLightBlue + } + } + + // ============================================================ + // LOGO SELECTION + // Returns appropriate logo based on theme + // ============================================================ + + /** + * Get the appropriate logo for the current theme + * @param whiteLogo The white version of the logo + * @param blackLogo The black version of the logo + * @return The logo that contrasts best with current theme + */ + PImage getLogo(PImage whiteLogo, PImage blackLogo) { + switch(currentTheme) { + case THEME_DARK: return whiteLogo; // White logo on dark background + case THEME_LIGHT: return blackLogo; // Black logo on light background + default: return whiteLogo; // White logo on blue background + } + } +} diff --git a/OpenBCI_GUI/TopNav.pde b/OpenBCI_GUI/TopNav.pde index 36be7ca7d..79aca7570 100644 --- a/OpenBCI_GUI/TopNav.pde +++ b/OpenBCI_GUI/TopNav.pde @@ -197,29 +197,19 @@ class TopNav { } void draw() { - PImage logo; - int logo_w = 128; - int logo_h = 22; - color topNavBg; - color subNavBg; + // Use the global style manager for theme-aware colors + PImage logo = style.getLogo(logo_white, logo_black); + color topNavBg = style.getTopNavBackground(); + color subNavBg = style.getSubNavBackground(); - if (colorScheme == COLOR_SCHEME_ALTERNATIVE_A) { - topNavBg = OPENBCI_BLUE; - subNavBg = SUBNAV_LIGHTBLUE; - logo = logo_white; - } else { - topNavBg = WHITE; - subNavBg = color(229); - logo = logo_black; - } + // Update stroke color based on theme + color currentStrokeColor = style.isDarkMode() ? style.getBoxStrokeColor() : strokeColor; //Draw background rectangles for TopNav and SubNav pushStyle(); - //stroke(OPENBCI_DARKBLUE); fill(topNavBg); rect(0, 0, width, navBarHeight); - //noStroke(); - stroke(strokeColor); + stroke(currentStrokeColor); fill(subNavBg); rect(-1, navBarHeight, width+2, navBarHeight); popStyle(); @@ -257,8 +247,8 @@ class TopNav { configSelector.draw(); tutorialSelector.draw(); - //Draw Console Log Image on top of cp5 object - PImage _logo = (colorScheme == COLOR_SCHEME_DEFAULT) ? consoleImgBlue : consoleImgWhite; + //Draw Console Log Image on top of cp5 object - use white for dark themes + PImage _logo = style.isDarkMode() ? consoleImgWhite : (style.getTheme() == THEME_LIGHT ? consoleImgBlue : consoleImgWhite); image(_logo, debugButton.getPosition()[0] + 6, debugButton.getPosition()[1] + 2, 22, 22); //Draw camera image on top of cp5 object image(screenshotImgWhite, screenshotButton.getPosition()[0] + 6, screenshotButton.getPosition()[1] + 2, 22, 22); diff --git a/OpenBCI_GUI/Widget.pde b/OpenBCI_GUI/Widget.pde index 4a0abafc3..86490308c 100644 --- a/OpenBCI_GUI/Widget.pde +++ b/OpenBCI_GUI/Widget.pde @@ -62,15 +62,15 @@ class Widget { public void draw(){ pushStyle(); noStroke(); - fill(255); - rect(x,y-1,w,h+1); //draw white widget background + fill(style.getWidgetBackground()); + rect(x,y-1,w,h+1); //draw widget background popStyle(); //draw nav bars and button bars pushStyle(); - fill(150, 150, 150); + fill(style.isDarkMode() ? style.getSubNavBackground() : color(150, 150, 150)); rect(x0, y0, w0, navH); //top bar - fill(200, 200, 200); + fill(style.isDarkMode() ? style.getBoxColor() : color(200, 200, 200)); rect(x0, y0+navH, w0, navH); //button bar popStyle(); } From f74669420dcc83c1e2f6ab74d89f6c0e89043d4a Mon Sep 17 00:00:00 2001 From: anaya yorke Date: Sat, 28 Feb 2026 04:10:41 -0500 Subject: [PATCH 4/7] feat: Implement dynamic dark mode theme system with comprehensive UI updates ## Core Theme System - Style.pde: Added theme change notification system via notifyThemeChange() - Style.pde: Added updateDropdownColors() to dynamically update global dropdown colors - Style.pde: Added graph-specific color getters ## Widget Infrastructure - Widget.pde: Added base updateColors() method - WidgetManager.pde: Added updateAllWidgetColors() to propagate theme changes ## Graph/Plot Dark Mode Support - TimeSeriesWidgetHelperClasses.pde: Added updatePlotColors() to ChannelBar - W_TimeSeries.pde: Added updateColors() override - W_FFT.pde: Added updatePlotColors() and updateColors() - W_Accelerometer.pde: Added updatePlotColors(), theme-aware circular display - W_BandPower.pde: Added updatePlotColors() and updateColors() - W_AnalogRead.pde: Added updatePlotColors() to AnalogReadBar - W_Marker.pde: Added updatePlotColors(), button and textfield theming ## Widget-Specific Fixes - Grid.pde: Added updateDefaultTextColor() for theme-aware tables - W_PacketLoss.pde: Fixed unreadable text - W_Focus.pde: Fixed status circle text, grid and audio buttons - AuditoryNeurofeedback.pde: Added updateColors() for buttons - FilterUI.pde: Added dark mode popup support ## TopNav Updates - TopNav.pde: Updated ConfigSelector for theme-aware Settings menu --- OpenBCI_GUI/AuditoryNeurofeedback.pde | 9 + OpenBCI_GUI/FilterUI.pde | 17 +- OpenBCI_GUI/Grid.pde | 11 +- OpenBCI_GUI/Style.pde | 233 ++++++++---------- OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde | 17 +- OpenBCI_GUI/TopNav.pde | 35 ++- OpenBCI_GUI/W_Accelerometer.pde | 39 ++- OpenBCI_GUI/W_AnalogRead.pde | 25 +- OpenBCI_GUI/W_BandPower.pde | 30 ++- OpenBCI_GUI/W_FFT.pde | 29 ++- OpenBCI_GUI/W_Focus.pde | 9 + OpenBCI_GUI/W_Marker.pde | 45 +++- OpenBCI_GUI/W_PacketLoss.pde | 9 +- OpenBCI_GUI/W_TimeSeries.pde | 9 + OpenBCI_GUI/Widget.pde | 6 + OpenBCI_GUI/WidgetManager.pde | 8 + 16 files changed, 359 insertions(+), 172 deletions(-) diff --git a/OpenBCI_GUI/AuditoryNeurofeedback.pde b/OpenBCI_GUI/AuditoryNeurofeedback.pde index ff180624c..04345cf6d 100644 --- a/OpenBCI_GUI/AuditoryNeurofeedback.pde +++ b/OpenBCI_GUI/AuditoryNeurofeedback.pde @@ -140,5 +140,14 @@ class AuditoryNeurofeedback { }); modeButton.setDescription("Change Auditory Feedback mode. Use the Metric to control all notes at once, or use Band Powers to control certain notes of the chord."); } + + public void updateColors() { + color btnBg = style.isDarkMode() ? style.getButtonColor() : colorNotPressed; + color btnText = style.isDarkMode() ? style.getButtonTextColor() : OPENBCI_DARKBLUE; + startStopButton.setColorBackground(btnBg); + startStopButton.getCaptionLabel().setColor(btnText); + modeButton.setColorBackground(btnBg); + modeButton.getCaptionLabel().setColor(btnText); + } } \ No newline at end of file diff --git a/OpenBCI_GUI/FilterUI.pde b/OpenBCI_GUI/FilterUI.pde index 2e1c86b26..ae32e80af 100644 --- a/OpenBCI_GUI/FilterUI.pde +++ b/OpenBCI_GUI/FilterUI.pde @@ -36,7 +36,9 @@ class FilterUIPopup extends PApplet implements Runnable { private color headerColor = OPENBCI_BLUE; private color buttonColor = OPENBCI_BLUE; + // backgroundColor will be set dynamically based on theme private color backgroundColor = GREY_235; + private color textLabelColor = color(102); private ControlP5 cp5; @@ -137,12 +139,25 @@ class FilterUIPopup extends PApplet implements Runnable { frame.toFront(); frame.requestFocus(); + // Set theme-aware colors + updateThemeColors(); + cp5 = new ControlP5(this); cp5.setGraphics(this, 0, 0); cp5.setAutoDraw(false); createAllCp5Objects(); } + + private void updateThemeColors() { + if (style.isDarkMode()) { + backgroundColor = style.getBoxColor(); + textLabelColor = style.getTextColor(); + } else { + backgroundColor = GREY_235; + textLabelColor = color(102); + } + } @Override void draw() { @@ -220,7 +235,7 @@ class FilterUIPopup extends PApplet implements Runnable { text("Notch", headerObjX[2], HEADER_OBJ_Y, HEADER_OBJ_WIDTH, uiObjectHeight); // Column labels textAlign(CENTER, TOP); - fill(102); + fill(textLabelColor); text("Channel", columnObjX[0], HEADER_HEIGHT + SM_SPACER, TEXTFIELD_WIDTH, HEADER_HEIGHT); String firstColumnHeader = ""; String secondColumnHeader = ""; diff --git a/OpenBCI_GUI/Grid.pde b/OpenBCI_GUI/Grid.pde index 69a996a52..9fff263b1 100644 --- a/OpenBCI_GUI/Grid.pde +++ b/OpenBCI_GUI/Grid.pde @@ -32,7 +32,12 @@ class Grid { strings = new String[numRows][numCols]; textColors = new color[numRows][numCols]; - color defaultTextColor = OPENBCI_DARKBLUE; + updateDefaultTextColor(); + } + + // Update default text color based on theme + public void updateDefaultTextColor() { + color defaultTextColor = style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE; for (color[] row: textColors) { Arrays.fill(row, defaultTextColor); } @@ -41,7 +46,7 @@ class Grid { public void draw() { pushStyle(); textAlign(LEFT); - stroke(OPENBCI_DARKBLUE); + stroke(style.isDarkMode() ? style.getGraphGridColor() : OPENBCI_DARKBLUE); textFont(tableFont, tableFontSize); if (drawTableInnerLines) { @@ -69,7 +74,7 @@ class Grid { if (drawTableBorder) { noFill(); - stroke(OPENBCI_DARKBLUE); + stroke(style.isDarkMode() ? style.getGraphGridColor() : OPENBCI_DARKBLUE); rect(x, y, w, rowOffset[numRows - 1]); } diff --git a/OpenBCI_GUI/Style.pde b/OpenBCI_GUI/Style.pde index fd7179b48..52a5d3d8a 100644 --- a/OpenBCI_GUI/Style.pde +++ b/OpenBCI_GUI/Style.pde @@ -8,14 +8,12 @@ // Themes available: // - THEME_DEFAULT: Original OpenBCI blue theme // - THEME_DARK: Dark mode for accessibility (reduced eye strain) -// - THEME_LIGHT: Light/white theme // /////////////////////////////////////////////////////////////////////////////////////// // Theme constants - these identify which theme is active final int THEME_DEFAULT = 0; // Original OpenBCI blue final int THEME_DARK = 1; // Dark mode -final int THEME_LIGHT = 2; // Light mode // Global style instance - use this throughout the GUI Style style; @@ -57,37 +55,28 @@ class Style { private final color DEFAULT_HELP_WIDGET_BG = color(31, 69, 110); // OPENBCI_BLUE private final color DEFAULT_HELP_WIDGET_TEXT_BG = color(1, 18, 41); - // --- THEME_DARK (Dark Mode) --- - private final color DARK_TOPNAV_BG = color(25, 25, 30); // Very dark blue-grey - private final color DARK_SUBNAV_BG = color(35, 35, 45); // Slightly lighter - private final color DARK_BOX_BG = color(45, 45, 55); // Dark grey for boxes - private final color DARK_BOX_STROKE = color(70, 70, 80); // Subtle border - private final color DARK_WIDGET_BG = color(30, 30, 38); // Dark widget background - private final color DARK_TEXT_PRIMARY = color(230, 230, 235); // Off-white text - private final color DARK_TEXT_SECONDARY = color(160, 160, 170); // Muted text - private final color DARK_TEXT_ON_DARK = color(230, 230, 235); // Same as primary in dark - private final color DARK_BUTTON_BG = color(60, 65, 80); // Dark button - private final color DARK_BUTTON_TEXT = color(230, 230, 235); // Light text on buttons - private final color DARK_BUTTON_HOVER = color(75, 80, 95); // Slightly lighter on hover - private final color DARK_BUTTON_PRESSED = color(50, 55, 70); // Darker when pressed - private final color DARK_HELP_WIDGET_BG = color(25, 25, 30); // Match topnav - private final color DARK_HELP_WIDGET_TEXT_BG = color(20, 20, 25); // Very dark - - // --- THEME_LIGHT (Light Mode) --- - private final color LIGHT_TOPNAV_BG = color(255); // White - private final color LIGHT_SUBNAV_BG = color(245, 245, 248); // Very light grey - private final color LIGHT_BOX_BG = color(250, 250, 252); // Almost white - private final color LIGHT_BOX_STROKE = color(200, 200, 205); // Light grey border - private final color LIGHT_WIDGET_BG = color(255); // White - private final color LIGHT_TEXT_PRIMARY = color(30, 30, 35); // Near black - private final color LIGHT_TEXT_SECONDARY = color(100, 100, 110); // Grey - private final color LIGHT_TEXT_ON_DARK = color(255); // White (for accent buttons) - private final color LIGHT_BUTTON_BG = color(31, 69, 110); // Keep OpenBCI blue for buttons - private final color LIGHT_BUTTON_TEXT = color(255); // White on blue buttons - private final color LIGHT_BUTTON_HOVER = color(45, 90, 140); // Lighter blue - private final color LIGHT_BUTTON_PRESSED = color(20, 50, 90); // Darker blue - private final color LIGHT_HELP_WIDGET_BG = color(245, 245, 248); // Light grey - private final color LIGHT_HELP_WIDGET_TEXT_BG = color(255); // White + // --- THEME_DARK (Dark Mode - OpenBCI colors but darker, near black) --- + // Based on OpenBCI blue (31, 69, 110) but much darker + private final color DARK_TOPNAV_BG = color(8, 18, 28); // Very dark OpenBCI blue (near black) + private final color DARK_SUBNAV_BG = color(12, 28, 45); // Slightly lighter dark blue + private final color DARK_BOX_BG = color(18, 35, 55); // Dark blue-grey for boxes + private final color DARK_BOX_STROKE = color(25, 50, 80); // Subtle blue border + private final color DARK_WIDGET_BG = color(10, 22, 35); // Very dark blue widget background + private final color DARK_TEXT_PRIMARY = color(200, 210, 220); // Soft off-white text (not harsh) + private final color DARK_TEXT_SECONDARY = color(140, 155, 170); // Muted blue-grey text + private final color DARK_TEXT_ON_DARK = color(200, 210, 220); // Same as primary in dark + private final color DARK_BUTTON_BG = color(20, 45, 75); // Dark OpenBCI blue button + private final color DARK_BUTTON_TEXT = color(200, 210, 220); // Soft light text on buttons + private final color DARK_BUTTON_HOVER = color(28, 58, 95); // Slightly lighter on hover + private final color DARK_BUTTON_PRESSED = color(15, 35, 60); // Darker when pressed + private final color DARK_HELP_WIDGET_BG = color(8, 18, 28); // Match topnav + private final color DARK_HELP_WIDGET_TEXT_BG = color(5, 12, 20); // Near black + + // --- Dimmed accent colors for dark mode (reduced contrast) --- + private final color DARK_SUCCESS = color(140, 190, 130); // Dimmed green + private final color DARK_ERROR = color(180, 70, 60); // Dimmed red + private final color DARK_WARNING = color(180, 145, 40); // Dimmed yellow + private final color DARK_ACCENT = color(45, 100, 160); // Dimmed OpenBCI blue // ============================================================ // CONSTRUCTOR @@ -108,10 +97,10 @@ class Style { /** * Set the current theme - * @param theme Use THEME_DEFAULT, THEME_DARK, or THEME_LIGHT + * @param theme Use THEME_DEFAULT or THEME_DARK */ void setTheme(int theme) { - if (theme >= THEME_DEFAULT && theme <= THEME_LIGHT) { + if (theme >= THEME_DEFAULT && theme <= THEME_DARK) { this.currentTheme = theme; println("Style: Theme changed to " + getThemeName()); } @@ -131,19 +120,50 @@ class Style { switch(currentTheme) { case THEME_DARK: return "Dark"; - case THEME_LIGHT: - return "Light"; default: return "Default"; } } /** - * Cycle to the next theme (useful for toggle buttons) + * Cycle to the next theme (toggle between Default and Dark) */ void cycleTheme() { - currentTheme = (currentTheme + 1) % 3; + currentTheme = (currentTheme + 1) % 2; // Only 2 themes now println("Style: Theme cycled to " + getThemeName()); + notifyThemeChange(); + } + + /** + * Notify all widgets that the theme has changed so they can update their colors + */ + void notifyThemeChange() { + // Update global dropdown colors + updateDropdownColors(); + + // Update all widget plot colors when theme changes + if (widgetManager != null) { + widgetManager.updateAllWidgetColors(); + } + } + + /** + * Update the global dropdown colors based on current theme + */ + void updateDropdownColors() { + if (isDarkMode()) { + dropdownColorsGlobal.setActive((int)color(15, 35, 60)); // bg when pressed + dropdownColorsGlobal.setForeground((int)color(25, 50, 80)); // hover + dropdownColorsGlobal.setBackground((int)color(18, 35, 55)); // bg of boxes + dropdownColorsGlobal.setCaptionLabel((int)color(200, 210, 220)); // text in primary + dropdownColorsGlobal.setValueLabel((int)color(180, 190, 200)); // text in dropdowns + } else { + dropdownColorsGlobal.setActive((int)BUTTON_PRESSED); + dropdownColorsGlobal.setForeground((int)BUTTON_HOVER); + dropdownColorsGlobal.setBackground((int)color(255)); + dropdownColorsGlobal.setCaptionLabel((int)color(1, 18, 41)); + dropdownColorsGlobal.setValueLabel((int)color(100)); + } } /** @@ -161,148 +181,116 @@ class Style { // --- Navigation Colors --- color getTopNavBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_TOPNAV_BG; - case THEME_LIGHT: return LIGHT_TOPNAV_BG; - default: return DEFAULT_TOPNAV_BG; - } + return isDarkMode() ? DARK_TOPNAV_BG : DEFAULT_TOPNAV_BG; } color getSubNavBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_SUBNAV_BG; - case THEME_LIGHT: return LIGHT_SUBNAV_BG; - default: return DEFAULT_SUBNAV_BG; - } + return isDarkMode() ? DARK_SUBNAV_BG : DEFAULT_SUBNAV_BG; } // --- Box/Panel Colors --- color getBoxColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BOX_BG; - case THEME_LIGHT: return LIGHT_BOX_BG; - default: return DEFAULT_BOX_BG; - } + return isDarkMode() ? DARK_BOX_BG : DEFAULT_BOX_BG; } color getBoxStrokeColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BOX_STROKE; - case THEME_LIGHT: return LIGHT_BOX_STROKE; - default: return DEFAULT_BOX_STROKE; - } + return isDarkMode() ? DARK_BOX_STROKE : DEFAULT_BOX_STROKE; } // --- Widget Colors --- color getWidgetBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_WIDGET_BG; - case THEME_LIGHT: return LIGHT_WIDGET_BG; - default: return DEFAULT_WIDGET_BG; - } + return isDarkMode() ? DARK_WIDGET_BG : DEFAULT_WIDGET_BG; } // --- Text Colors --- color getTextColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_TEXT_PRIMARY; - case THEME_LIGHT: return LIGHT_TEXT_PRIMARY; - default: return DEFAULT_TEXT_PRIMARY; - } + return isDarkMode() ? DARK_TEXT_PRIMARY : DEFAULT_TEXT_PRIMARY; } color getSecondaryTextColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_TEXT_SECONDARY; - case THEME_LIGHT: return LIGHT_TEXT_SECONDARY; - default: return DEFAULT_TEXT_SECONDARY; - } + return isDarkMode() ? DARK_TEXT_SECONDARY : DEFAULT_TEXT_SECONDARY; } color getTextOnDarkBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_TEXT_ON_DARK; - case THEME_LIGHT: return LIGHT_TEXT_ON_DARK; - default: return DEFAULT_TEXT_ON_DARK; - } + return isDarkMode() ? DARK_TEXT_ON_DARK : DEFAULT_TEXT_ON_DARK; } // --- Button Colors --- color getButtonColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BUTTON_BG; - case THEME_LIGHT: return LIGHT_BUTTON_BG; - default: return DEFAULT_BUTTON_BG; - } + return isDarkMode() ? DARK_BUTTON_BG : DEFAULT_BUTTON_BG; } color getButtonTextColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BUTTON_TEXT; - case THEME_LIGHT: return LIGHT_BUTTON_TEXT; - default: return DEFAULT_BUTTON_TEXT; - } + return isDarkMode() ? DARK_BUTTON_TEXT : DEFAULT_BUTTON_TEXT; } color getButtonHoverColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BUTTON_HOVER; - case THEME_LIGHT: return LIGHT_BUTTON_HOVER; - default: return DEFAULT_BUTTON_HOVER; - } + return isDarkMode() ? DARK_BUTTON_HOVER : DEFAULT_BUTTON_HOVER; } color getButtonPressedColor() { - switch(currentTheme) { - case THEME_DARK: return DARK_BUTTON_PRESSED; - case THEME_LIGHT: return LIGHT_BUTTON_PRESSED; - default: return DEFAULT_BUTTON_PRESSED; - } + return isDarkMode() ? DARK_BUTTON_PRESSED : DEFAULT_BUTTON_PRESSED; } // --- Help Widget (Console) Colors --- color getHelpWidgetBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_HELP_WIDGET_BG; - case THEME_LIGHT: return LIGHT_HELP_WIDGET_BG; - default: return DEFAULT_HELP_WIDGET_BG; - } + return isDarkMode() ? DARK_HELP_WIDGET_BG : DEFAULT_HELP_WIDGET_BG; } color getHelpWidgetTextBackground() { - switch(currentTheme) { - case THEME_DARK: return DARK_HELP_WIDGET_TEXT_BG; - case THEME_LIGHT: return LIGHT_HELP_WIDGET_TEXT_BG; - default: return DEFAULT_HELP_WIDGET_TEXT_BG; - } + return isDarkMode() ? DARK_HELP_WIDGET_TEXT_BG : DEFAULT_HELP_WIDGET_TEXT_BG; + } + + // --- Graph/Plot Colors --- + + color getGraphBackground() { + // The outer background of plots + return isDarkMode() ? DARK_WIDGET_BG : color(255); + } + + color getGraphBoxBackground() { + // The inner box background of plots (where data is drawn) + return isDarkMode() ? color(12, 25, 40) : color(245); } - // --- Special Colors (these stay consistent across themes for recognition) --- + color getGraphGridColor() { + return isDarkMode() ? color(35, 55, 80) : color(210); + } + + color getGraphLineColor() { + return isDarkMode() ? color(45, 70, 100) : color(210); + } + + color getGraphAxisColor() { + // Axis text and lines + return isDarkMode() ? color(160, 175, 190) : OPENBCI_DARKBLUE; + } + + // --- Special Colors (dimmed in dark mode for reduced contrast) --- color getSuccessColor() { - return color(195, 242, 181); // TURN_ON_GREEN - always green + // Green - dimmed in dark mode + return isDarkMode() ? DARK_SUCCESS : color(195, 242, 181); } color getErrorColor() { - return color(224, 56, 45); // BOLD_RED - always red + // Red - dimmed in dark mode + return isDarkMode() ? DARK_ERROR : color(224, 56, 45); } color getWarningColor() { - return color(221, 178, 13); // SIGNAL_CHECK_YELLOW - always yellow + // Yellow - dimmed in dark mode + return isDarkMode() ? DARK_WARNING : color(221, 178, 13); } color getAccentColor() { - // OpenBCI blue accent - slightly adjusted for dark mode visibility - switch(currentTheme) { - case THEME_DARK: return color(70, 130, 200); // Brighter blue for dark bg - case THEME_LIGHT: return color(31, 69, 110); // Original OPENBCI_BLUE - default: return color(57, 128, 204); // buttonsLightBlue - } + // OpenBCI blue accent - dimmed in dark mode + return isDarkMode() ? DARK_ACCENT : color(57, 128, 204); } // ============================================================ @@ -317,10 +305,7 @@ class Style { * @return The logo that contrasts best with current theme */ PImage getLogo(PImage whiteLogo, PImage blackLogo) { - switch(currentTheme) { - case THEME_DARK: return whiteLogo; // White logo on dark background - case THEME_LIGHT: return blackLogo; // Black logo on light background - default: return whiteLogo; // White logo on blue background - } + // White logo works for both Default (blue bg) and Dark (dark bg) + return whiteLogo; } } diff --git a/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde b/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde index 994cba5c4..385b97975 100644 --- a/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde +++ b/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde @@ -75,9 +75,8 @@ class ChannelBar { plot.setPointSize(2); plot.setPointColor(0); plot.setAllFontProperties("Arial", 0, 14); - plot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors to plot + updatePlotColors(); if (channelIndex == globalChannelCount-1) { plot.getXAxis().setAxisLabelText("Time (s)"); plot.getXAxis().getAxisLabel().setOffset(plotBottomWellH/2 + 5f); @@ -348,6 +347,18 @@ class ChannelBar { boolean isLastChannel = channelIndex == widgetManager.getTimeSeriesWidget().tsChanSelect.getActiveChannels().get(numActiveChannels - 1); return isLastChannel; } + + public void updatePlotColors() { + // Apply theme colors to the plot + color axisColor = style.getGraphAxisColor(); + plot.setBgColor(style.getGraphBackground()); + plot.setBoxBgColor(style.getGraphBoxBackground()); + plot.setBoxLineColor(style.getGraphLineColor()); + plot.setGridLineColor(style.getGraphGridColor()); + plot.getXAxis().setFontColor(axisColor); + plot.getXAxis().setLineColor(axisColor); + plot.getXAxis().getAxisLabel().setFontColor(axisColor); + } public void mousePressed() { } diff --git a/OpenBCI_GUI/TopNav.pde b/OpenBCI_GUI/TopNav.pde index 79aca7570..416e02a3c 100644 --- a/OpenBCI_GUI/TopNav.pde +++ b/OpenBCI_GUI/TopNav.pde @@ -247,9 +247,8 @@ class TopNav { configSelector.draw(); tutorialSelector.draw(); - //Draw Console Log Image on top of cp5 object - use white for dark themes - PImage _logo = style.isDarkMode() ? consoleImgWhite : (style.getTheme() == THEME_LIGHT ? consoleImgBlue : consoleImgWhite); - image(_logo, debugButton.getPosition()[0] + 6, debugButton.getPosition()[1] + 2, 22, 22); + //Draw Console Log Image on top of cp5 object - white for both themes (dark bg) + image(consoleImgWhite, debugButton.getPosition()[0] + 6, debugButton.getPosition()[1] + 2, 22, 22); //Draw camera image on top of cp5 object image(screenshotImgWhite, screenshotButton.getPosition()[0] + 6, screenshotButton.getPosition()[1] + 2, 22, 22); } @@ -720,6 +719,7 @@ class ConfigSelector { private boolean clearAllSettingsPressed; public boolean isVisible; private ControlP5 settings_cp5; + private Button themeToggle; private Button expertMode; private Button autoStartDataStream; private Button autoStartNetworkStream; @@ -745,7 +745,7 @@ class ConfigSelector { margin = 6; b_w = w - margin*2; b_h = 22; - h = margin*10 + b_h*9; + h = margin*11 + b_h*10; //makes the setting text "are you sure" display correctly on linux osPadding = isLinux() ? -3 : -2; osPadding2 = isLinux() ? 5 : 0; @@ -758,6 +758,8 @@ class ConfigSelector { isVisible = false; int buttonNumber = 0; + createThemeToggleButton("themeToggle", getThemeButtonText(), x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h); + buttonNumber++; createExpertModeButton("expertMode", "Turn Expert Mode On", x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h); buttonNumber++; createAutoStartDataStreamButton("autoStartDataStream", "Auto Start Data Stream", x + margin, y + margin*(buttonNumber+1) + b_h*(buttonNumber), b_w, b_h); @@ -781,8 +783,9 @@ class ConfigSelector { public void draw() { if (isVisible) { //only draw if visible - color strokeColor = OPENBCI_DARKBLUE; - color fillColor = SUBNAV_LIGHTBLUE; + // Use theme-aware colors + color strokeColor = style.isDarkMode() ? style.getBoxStrokeColor() : OPENBCI_DARKBLUE; + color fillColor = style.isDarkMode() ? style.getBoxColor() : SUBNAV_LIGHTBLUE; pushStyle(); @@ -797,7 +800,7 @@ class ConfigSelector { if (clearAllSettingsPressed) { textFont(p2, 16); - fill(255); + fill(style.isDarkMode() ? style.getTextColor() : 255); textAlign(CENTER); text("Are You Sure?", x + w/2, clearAllGUISettings.getPosition()[1] + b_h*2); } @@ -837,7 +840,7 @@ class ConfigSelector { x = width - w - 3; int dx = oldX - x; - h = !isSessionStarted ? margin*6 + b_h*5 : margin*9 + b_h*8; + h = !isSessionStarted ? margin*7 + b_h*6 : margin*10 + b_h*9; //Update the Y position for the clear settings buttons float clearSettingsButtonY = !isSessionStarted ? @@ -922,6 +925,22 @@ class ConfigSelector { }); } + private void createThemeToggleButton(String name, String text, int _x, int _y, int _w, int _h) { + themeToggle = createButton(settings_cp5, name, text, _x, _y, _w, _h, p5, 12, BUTTON_EXPERTPURPLE, WHITE); + themeToggle.onRelease(new CallbackListener() { + public void controlEvent(CallbackEvent theEvent) { + style.cycleTheme(); + themeToggle.getCaptionLabel().setText(getThemeButtonText()); + output("Theme changed to: " + style.getThemeName()); + } + }); + themeToggle.setDescription("Toggle between Default, Dark, and Light themes for the GUI."); + } + + private String getThemeButtonText() { + return "Theme: " + style.getThemeName(); + } + private void createExpertModeButton(String name, String text, int _x, int _y, int _w, int _h) { expertMode = createButton(settings_cp5, name, text, _x, _y, _w, _h, p5, 12, BUTTON_NOOBGREEN, WHITE); expertMode.onRelease(new CallbackListener() { diff --git a/OpenBCI_GUI/W_Accelerometer.pde b/OpenBCI_GUI/W_Accelerometer.pde index 1fc78c17d..0967654a7 100644 --- a/OpenBCI_GUI/W_Accelerometer.pde +++ b/OpenBCI_GUI/W_Accelerometer.pde @@ -122,18 +122,20 @@ class W_Accelerometer extends WidgetWithSettings { pushStyle(); - fill(50); + // Use theme-aware colors for labels + fill(style.isDarkMode() ? style.getSecondaryTextColor() : 50); textFont(p4, 14); textAlign(CENTER,CENTER); text("z", polarWindowX, (polarWindowY-polarWindowHeight/2)-12); text("x", (polarWindowX+polarWindowWidth/2)+8, polarWindowY-5); text("y", (polarWindowX+polarCorner)+10, (polarWindowY-polarCorner)-10); - fill(graphBG); //pulse window background - stroke(graphStroke); + // Use theme-aware colors for circular display + fill(style.isDarkMode() ? style.getGraphBoxBackground() : graphBG); + stroke(style.isDarkMode() ? style.getGraphLineColor() : graphStroke); ellipse(polarWindowX,polarWindowY,polarWindowWidth,polarWindowHeight); - stroke(180); + stroke(style.isDarkMode() ? style.getGraphGridColor() : 180); line(polarWindowX-polarWindowWidth/2, polarWindowY, polarWindowX+polarWindowWidth/2, polarWindowY); line(polarWindowX, polarWindowY-polarWindowHeight/2, polarWindowX, polarWindowY+polarWindowHeight/2); line(polarWindowX-polarCorner, polarWindowY+polarCorner, polarWindowX+polarCorner, polarWindowY-polarCorner); @@ -176,6 +178,12 @@ class W_Accelerometer extends WidgetWithSettings { accelModeButton.setPosition((int)(x0 + 1), (int)(y0 + NAV_HEIGHT + 1)); } + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + accelerometerBar.updatePlotColors(); + } + void mousePressed() { super.mousePressed(); } @@ -352,12 +360,8 @@ class AccelerometerBar { plot.setAllFontProperties("Arial", 0, 14); plot.getXAxis().getAxisLabel().setOffset(float(accBarPadding)); plot.getYAxis().getAxisLabel().setOffset(float(accBarPadding)); - plot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); - plot.getYAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getYAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getYAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors + updatePlotColors(); gplotAutoscaler = new GPlotAutoscaler(false, AUTOSCALE_SPACING); initArrays(); @@ -474,4 +478,19 @@ class AccelerometerBar { plot.setDim(w - 36 - 4, h); } + + void updatePlotColors() { + // Apply theme colors to the accelerometer plot + color axisColor = style.getGraphAxisColor(); + plot.setBgColor(style.getGraphBackground()); + plot.setBoxBgColor(style.getGraphBoxBackground()); + plot.setBoxLineColor(style.getGraphLineColor()); + plot.setGridLineColor(style.getGraphGridColor()); + plot.getXAxis().setFontColor(axisColor); + plot.getXAxis().setLineColor(axisColor); + plot.getXAxis().getAxisLabel().setFontColor(axisColor); + plot.getYAxis().setFontColor(axisColor); + plot.getYAxis().setLineColor(axisColor); + plot.getYAxis().getAxisLabel().setFontColor(axisColor); + } }; //end of class diff --git a/OpenBCI_GUI/W_AnalogRead.pde b/OpenBCI_GUI/W_AnalogRead.pde index 9ec3f4123..9c5d8b45e 100644 --- a/OpenBCI_GUI/W_AnalogRead.pde +++ b/OpenBCI_GUI/W_AnalogRead.pde @@ -128,6 +128,14 @@ class W_AnalogRead extends WidgetWithSettings { analogModeButton.setPosition((int)(x0 + 1), (int)(y0 + NAV_HEIGHT + 1)); } + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + for (AnalogReadBar bar : analogReadBars) { + bar.updatePlotColors(); + } + } + public void mousePressed() { super.mousePressed(); } @@ -270,9 +278,8 @@ class AnalogReadBar{ plot.setPointSize(2); plot.setPointColor(0); plot.setAllFontProperties("Arial", 0, 14); - plot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors + updatePlotColors(); if(auxValuesPosition == 2) { plot.getXAxis().setAxisLabelText("Time (s)"); } @@ -440,4 +447,16 @@ class AnalogReadBar{ digitalPin.x = analogPin.x; digitalPin.y = analogPin.y + 12; } + + void updatePlotColors() { + // Apply theme colors to the analog read plot + color axisColor = style.getGraphAxisColor(); + plot.setBgColor(style.getGraphBackground()); + plot.setBoxBgColor(style.getGraphBoxBackground()); + plot.setBoxLineColor(style.getGraphLineColor()); + plot.setGridLineColor(style.getGraphGridColor()); + plot.getXAxis().setFontColor(axisColor); + plot.getXAxis().setLineColor(axisColor); + plot.getXAxis().getAxisLabel().setFontColor(axisColor); + } }; diff --git a/OpenBCI_GUI/W_BandPower.pde b/OpenBCI_GUI/W_BandPower.pde index 7043a04f6..35c5e137f 100644 --- a/OpenBCI_GUI/W_BandPower.pde +++ b/OpenBCI_GUI/W_BandPower.pde @@ -95,12 +95,8 @@ class W_BandPower extends WidgetWithSettings { bp_plot.getXAxis().getAxisLabel().setOffset(42f); bp_plot.startHistograms(GPlot.VERTICAL); bp_plot.getHistogram().setDrawLabels(true); - bp_plot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - bp_plot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - bp_plot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); - bp_plot.getYAxis().setFontColor(OPENBCI_DARKBLUE); - bp_plot.getYAxis().setLineColor(OPENBCI_DARKBLUE); - bp_plot.getYAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors + updatePlotColors(); //setting border of histograms to match BG bp_plot.getHistogram().setLineColors(new color[]{ @@ -249,6 +245,28 @@ class W_BandPower extends WidgetWithSettings { widgetSettings.set(FFTFilteredEnum.class, _filteredEnum); updateDropdownLabel(FFTFilteredEnum.class, "bandPowerDataFilteringDropdown"); } + + private void updatePlotColors() { + // Apply theme colors to the BandPower plot + color axisColor = style.getGraphAxisColor(); + bp_plot.setBgColor(style.getGraphBackground()); + bp_plot.setBoxBgColor(style.getGraphBoxBackground()); + bp_plot.setBoxLineColor(style.getGraphLineColor()); + bp_plot.setGridLineColor(style.getGraphGridColor()); + bp_plot.getXAxis().setFontColor(axisColor); + bp_plot.getXAxis().setLineColor(axisColor); + bp_plot.getXAxis().getAxisLabel().setFontColor(axisColor); + bp_plot.getYAxis().setFontColor(axisColor); + bp_plot.getYAxis().setLineColor(axisColor); + bp_plot.getYAxis().getAxisLabel().setFontColor(axisColor); + bp_plot.getHistogram().setFontColor(axisColor); + } + + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + updatePlotColors(); + } }; public void bandPowerVerticalScaleDropdown(int n) { diff --git a/OpenBCI_GUI/W_FFT.pde b/OpenBCI_GUI/W_FFT.pde index fdf7ba292..8f539f583 100644 --- a/OpenBCI_GUI/W_FFT.pde +++ b/OpenBCI_GUI/W_FFT.pde @@ -100,12 +100,8 @@ class W_Fft extends WidgetWithSettings { fftPlot.getYAxis().setDrawTickLabels(true); fftPlot.setPointSize(2); fftPlot.setPointColor(0); - fftPlot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - fftPlot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - fftPlot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); - fftPlot.getYAxis().setFontColor(OPENBCI_DARKBLUE); - fftPlot.getYAxis().setLineColor(OPENBCI_DARKBLUE); - fftPlot.getYAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors + updatePlotColors(); //setup points of fft point arrays for (int i = 0; i < fftGplotPoints.length; i++) { @@ -255,6 +251,27 @@ class W_Fft extends WidgetWithSettings { widgetSettings.set(FFTFilteredEnum.class, _filteredEnum); updateDropdownLabel(FFTFilteredEnum.class, "fftFilteringDropdown"); } + + private void updatePlotColors() { + // Apply theme colors to the FFT plot + color axisColor = style.getGraphAxisColor(); + fftPlot.setBgColor(style.getGraphBackground()); + fftPlot.setBoxBgColor(style.getGraphBoxBackground()); + fftPlot.setBoxLineColor(style.getGraphLineColor()); + fftPlot.setGridLineColor(style.getGraphGridColor()); + fftPlot.getXAxis().setFontColor(axisColor); + fftPlot.getXAxis().setLineColor(axisColor); + fftPlot.getXAxis().getAxisLabel().setFontColor(axisColor); + fftPlot.getYAxis().setFontColor(axisColor); + fftPlot.getYAxis().setLineColor(axisColor); + fftPlot.getYAxis().getAxisLabel().setFontColor(axisColor); + } + + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + updatePlotColors(); + } }; //These functions need to be global! These functions are activated when an item from the corresponding dropdown is selected diff --git a/OpenBCI_GUI/W_Focus.pde b/OpenBCI_GUI/W_Focus.pde index fae7599eb..6cf6b91a3 100644 --- a/OpenBCI_GUI/W_Focus.pde +++ b/OpenBCI_GUI/W_Focus.pde @@ -213,6 +213,13 @@ class W_Focus extends WidgetWithSettings { super.mousePressed(); focusChanSelect.mousePressed(this.dropdownIsActive); //Calls channel select mousePressed and checks if clicked } + + @Override + public void updateColors() { + super.updateColors(); + dataGrid.updateDefaultTextColor(); + auditoryNeurofeedback.updateColors(); + } private void resizeTable() { int extraPadding = focusChanSelect.isVisible() ? NAV_HEIGHT : 0; @@ -329,6 +336,8 @@ class W_Focus extends WidgetWithSettings { ellipse(xc, yc, wc, hc); noStroke(); textAlign(CENTER); + // Use theme-aware text color for status label + fill(predictionExceedsThreshold ? cFocus : (style.isDarkMode() ? style.getTextColor() : cDark)); text(sb.toString(), xc, yc + hc/2 + 16); popStyle(); } diff --git a/OpenBCI_GUI/W_Marker.pde b/OpenBCI_GUI/W_Marker.pde index 9aa472ed9..aa455c760 100644 --- a/OpenBCI_GUI/W_Marker.pde +++ b/OpenBCI_GUI/W_Marker.pde @@ -148,6 +148,28 @@ class W_Marker extends WidgetWithSettings { localCP5.draw(); } + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + markerBar.updatePlotColors(); + // Update button colors + color btnBg = style.isDarkMode() ? style.getButtonColor() : colorNotPressed; + color btnText = style.isDarkMode() ? style.getButtonTextColor() : OPENBCI_DARKBLUE; + for (Button btn : markerButtons) { + btn.setColorBackground(btnBg); + btn.getCaptionLabel().setColor(btnText); + } + // Update textfield colors + color tfBg = style.isDarkMode() ? style.getBoxColor() : color(255); + color tfText = style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE; + markerReceiveIPTextfield.setColorBackground(tfBg); + markerReceiveIPTextfield.setColorValueLabel(tfText); + markerReceiveIPTextfield.setColor(tfText); + markerReceivePortTextfield.setColorBackground(tfBg); + markerReceivePortTextfield.setColorValueLabel(tfText); + markerReceivePortTextfield.setColor(tfText); + } + public void screenResized(){ super.screenResized(); @@ -475,12 +497,8 @@ class MarkerBar { plot.setAllFontProperties("Arial", 0, 14); plot.getXAxis().getAxisLabel().setOffset(float(X_AXIS_PADDING)); plot.getYAxis().getAxisLabel().setOffset(float(Y_AXIS_PADDING)); - plot.getXAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getXAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getXAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); - plot.getYAxis().setFontColor(OPENBCI_DARKBLUE); - plot.getYAxis().setLineColor(OPENBCI_DARKBLUE); - plot.getYAxis().getAxisLabel().setFontColor(OPENBCI_DARKBLUE); + // Apply theme colors + updatePlotColors(); gplotAutoscaler = new GPlotAutoscaler(false, AUTOSCALE_SPACING); initArrays(); @@ -573,5 +591,20 @@ class MarkerBar { plot.setDim(w - 36 - 4, h); } + + public void updatePlotColors() { + // Apply theme colors to the marker plot + color axisColor = style.getGraphAxisColor(); + plot.setBgColor(style.getGraphBackground()); + plot.setBoxBgColor(style.getGraphBoxBackground()); + plot.setBoxLineColor(style.getGraphLineColor()); + plot.setGridLineColor(style.getGraphGridColor()); + plot.getXAxis().setFontColor(axisColor); + plot.getXAxis().setLineColor(axisColor); + plot.getXAxis().getAxisLabel().setFontColor(axisColor); + plot.getYAxis().setFontColor(axisColor); + plot.getYAxis().setLineColor(axisColor); + plot.getYAxis().getAxisLabel().setFontColor(axisColor); + } }; diff --git a/OpenBCI_GUI/W_PacketLoss.pde b/OpenBCI_GUI/W_PacketLoss.pde index f8d8be27d..98b9791c3 100644 --- a/OpenBCI_GUI/W_PacketLoss.pde +++ b/OpenBCI_GUI/W_PacketLoss.pde @@ -133,7 +133,7 @@ class W_PacketLoss extends Widget { super.draw(); pushStyle(); - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); textFont(p5, 12); text("Session length: " + sessionTimeElapsed.toString(), x + PADDING_5, y + 15); text("Stream length: " + streamTimeElapsed.toString(), x + PADDING_5, y + 35); @@ -141,6 +141,12 @@ class W_PacketLoss extends Widget { dataGrid.draw(); } + + @Override + public void updateColors() { + super.updateColors(); + dataGrid.updateDefaultTextColor(); + } public void screenResized(){ super.screenResized(); @@ -168,5 +174,4 @@ class W_PacketLoss extends Widget { private void resizeGrid() { dataGrid.setDim(x, y + TOP_PADDING, w); } - }; diff --git a/OpenBCI_GUI/W_TimeSeries.pde b/OpenBCI_GUI/W_TimeSeries.pde index 26a1d4043..12bd19915 100644 --- a/OpenBCI_GUI/W_TimeSeries.pde +++ b/OpenBCI_GUI/W_TimeSeries.pde @@ -293,6 +293,15 @@ class W_TimeSeries extends WidgetWithSettings { } + @Override + public void updateColors() { + super.updateColors(); // Update dropdown colors + // Update all channel bar plot colors when theme changes + for (ChannelBar cb : channelBars) { + cb.updatePlotColors(); + } + } + void mousePressed() { super.mousePressed(); tsChanSelect.mousePressed(this.dropdownIsActive); //Calls channel select mousePressed and checks if clicked diff --git a/OpenBCI_GUI/Widget.pde b/OpenBCI_GUI/Widget.pde index 86490308c..309e47d7d 100644 --- a/OpenBCI_GUI/Widget.pde +++ b/OpenBCI_GUI/Widget.pde @@ -74,6 +74,12 @@ class Widget { rect(x0, y0+navH, w0, navH); //button bar popStyle(); } + + // Called when theme changes - override in subclasses to update plot colors + public void updateColors() { + // Update dropdown colors for this widget + cp5_widget.setColor(dropdownColorsGlobal); + } public void addDropdown(String _id, String _title, List _items, int _defaultItem){ NavBarDropdown dropdownToAdd = new NavBarDropdown(_id, _title, _items, _defaultItem); diff --git a/OpenBCI_GUI/WidgetManager.pde b/OpenBCI_GUI/WidgetManager.pde index 9363c9b81..b1f6fee12 100644 --- a/OpenBCI_GUI/WidgetManager.pde +++ b/OpenBCI_GUI/WidgetManager.pde @@ -349,6 +349,14 @@ class WidgetManager { return allWidgetSettings.toString(); } + public void updateAllWidgetColors() { + // Called when theme changes to update all widget colors + for (Widget widget : widgets) { + widget.updateColors(); + } + println("WidgetManager: Updated colors for all widgets"); + } + public void loadWidgetSettingsFromJson(String widgetSettingsJson) { JSONObject json = parseJSONObject(widgetSettingsJson); if (json == null) { From 728896c1593687dad972f5f2ba80dea19dc21df7 Mon Sep 17 00:00:00 2001 From: anaya yorke Date: Sat, 28 Feb 2026 04:25:18 -0500 Subject: [PATCH 5/7] feat: Dark Mode v0.5 - Fix text colors for readability in dark theme ## Widget Dropdown Labels (Widget.pde) - Fixed dropdown title labels (Vert Scale, Window, Labels, Max Hz, Max uV, Log/Lin, Smooth, Filter) - Now uses style.getTextColor() in dark mode instead of hardcoded OPENBCI_DARKBLUE ## Channel Select (ChannelSelect.pde) - Fixed 'Channels' label text color for dark mode readability ## Time Series Playback (TimeSeriesWidgetHelperClasses.pde) - Fixed playback scrollbar indicator color - Fixed timestamp text above scrollbar - Fixed TimeDisplay class timestamp text at bottom of widget ## EMG Widget (W_EMG.pde) - Fixed channel number labels in EMG grid cells ## EMG Joystick Widget (W_EMGJoystick.pde) - Fixed channel labels text color All text now uses theme-aware colors: style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE --- OpenBCI_GUI/ChannelSelect.pde | 2 +- OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde | 8 ++++---- OpenBCI_GUI/W_DigitalRead.pde | 2 +- OpenBCI_GUI/W_EMG.pde | 4 ++-- OpenBCI_GUI/W_EMGJoystick.pde | 2 +- OpenBCI_GUI/Widget.pde | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/OpenBCI_GUI/ChannelSelect.pde b/OpenBCI_GUI/ChannelSelect.pde index 963101cfd..2e0bd5f19 100644 --- a/OpenBCI_GUI/ChannelSelect.pde +++ b/OpenBCI_GUI/ChannelSelect.pde @@ -49,7 +49,7 @@ class ChannelSelect { if (channelSelectHover) { fill(OPENBCI_BLUE); } else { - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); } textFont(p5, 12); diff --git a/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde b/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde index 385b97975..02aad352e 100644 --- a/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde +++ b/OpenBCI_GUI/TimeSeriesWidgetHelperClasses.pde @@ -616,16 +616,16 @@ class PlaybackScrollbar { //select color for playback indicator if (over || locked) { - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); } else { - fill(102, 102, 102); + fill(style.isDarkMode() ? style.getSecondaryTextColor() : color(102, 102, 102)); } //draws playback position indicator rect(spos, ypos, sheight/2, sheight); //draw current timestamp and X of Y Seconds above scrollbar textFont(p2, 18); - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); textAlign(LEFT, TOP); float textHeight = textAscent() - textDescent(); float textY = y - textHeight - 10; @@ -706,7 +706,7 @@ class TimeDisplay { if (!currentAbsoluteTimeToDisplay.equals(null)) { int fontSize = 17; textFont(p2, fontSize); - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); float tw = textWidth(currentAbsoluteTimeToDisplay); text(currentAbsoluteTimeToDisplay, xpos + swidth - tw, ypos); text(streamTimeElapsed.toString(), xpos + 10, ypos); diff --git a/OpenBCI_GUI/W_DigitalRead.pde b/OpenBCI_GUI/W_DigitalRead.pde index 4e6272427..22128d38b 100644 --- a/OpenBCI_GUI/W_DigitalRead.pde +++ b/OpenBCI_GUI/W_DigitalRead.pde @@ -195,7 +195,7 @@ class DigitalReadDot{ color dotStroke = #d2d2d2; color dot0Fill = #f5f5f5; color dot1Fill = #f5f5f5; - color val0Fill = OPENBCI_DARKBLUE; + color val0Fill = OPENBCI_DARKBLUE; // Note: This is used for digital indicator, may need dynamic update color val1Fill = WHITE; int dotX; diff --git a/OpenBCI_GUI/W_EMG.pde b/OpenBCI_GUI/W_EMG.pde index f53a6bc82..a5538f094 100644 --- a/OpenBCI_GUI/W_EMG.pde +++ b/OpenBCI_GUI/W_EMG.pde @@ -165,8 +165,8 @@ class W_Emg extends WidgetWithSettings { //draw channel number at upper left corner of row/column cell pushStyle(); - stroke(OPENBCI_DARKBLUE); - fill(OPENBCI_DARKBLUE); + stroke(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); textFont(h4, 14); text((channel + 1), 10, 20); popStyle(); diff --git a/OpenBCI_GUI/W_EMGJoystick.pde b/OpenBCI_GUI/W_EMGJoystick.pde index 203d1e8ba..8302954f8 100644 --- a/OpenBCI_GUI/W_EMGJoystick.pde +++ b/OpenBCI_GUI/W_EMGJoystick.pde @@ -309,7 +309,7 @@ class W_EmgJoystick extends WidgetWithSettings { private void drawChannelLabels() { pushStyle(); - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); textFont(p4, 14); textLeading(14); textAlign(CENTER,CENTER); diff --git a/OpenBCI_GUI/Widget.pde b/OpenBCI_GUI/Widget.pde index 309e47d7d..e08082ace 100644 --- a/OpenBCI_GUI/Widget.pde +++ b/OpenBCI_GUI/Widget.pde @@ -195,7 +195,7 @@ class Widget { textFont(h5); textSize(12); textAlign(CENTER, BOTTOM); - fill(OPENBCI_DARKBLUE); + fill(style.isDarkMode() ? style.getTextColor() : OPENBCI_DARKBLUE); for(int i = 0; i < dropdowns.size(); i++){ int dropdownPos = dropdowns.size() - i; int _width = cp5_widget.getController(dropdowns.get(i).id).getWidth(); From 131f3bb863f2ac6095ed7e52bbbf8dcfc51f421a Mon Sep 17 00:00:00 2001 From: anaya yorke Date: Tue, 3 Mar 2026 00:30:30 -0500 Subject: [PATCH 6/7] feat: Dark Mode v0.6 - ThemeType enum, Light mode, theme persistence Style.pde: Added ThemeType enum (DEFAULT/LIGHT/DARK) with next() cycling Style.pde: Added Light mode color palette, all getters use 3-way switch GuiSettings.pde: Added themeType to GuiSettingsValues for JSON persistence GuiSettings.pde: Added setThemeType/getThemeType/applyThemeFromSettings Widget.pde: Nav bar colors handle all 3 themes properly ChannelSelect.pde: Channels background uses style.getBoxColor() Architecture aligned with PR #1063 ThemeType pattern, no FrontendTheme.pde needed --- OpenBCI_GUI/ChannelSelect.pde | 2 +- OpenBCI_GUI/GuiSettings.pde | 25 ++++ OpenBCI_GUI/Style.pde | 265 ++++++++++++++++++++++++++-------- OpenBCI_GUI/Widget.pde | 4 +- 4 files changed, 230 insertions(+), 66 deletions(-) diff --git a/OpenBCI_GUI/ChannelSelect.pde b/OpenBCI_GUI/ChannelSelect.pde index 2e0bd5f19..d4e3514bb 100644 --- a/OpenBCI_GUI/ChannelSelect.pde +++ b/OpenBCI_GUI/ChannelSelect.pde @@ -100,7 +100,7 @@ class ChannelSelect { public void drawGrayBackground(int _x, int _y, int _w, int _h) { pushStyle(); - fill(200); + fill(style.isDarkMode() ? style.getBoxColor() : 200); rect(_x, _y, _w, _h); popStyle(); } diff --git a/OpenBCI_GUI/GuiSettings.pde b/OpenBCI_GUI/GuiSettings.pde index c492433c1..80b4a1f76 100644 --- a/OpenBCI_GUI/GuiSettings.pde +++ b/OpenBCI_GUI/GuiSettings.pde @@ -40,6 +40,7 @@ public class GuiSettingsValues { public boolean autoStartDataStream = false; public boolean autoStartNetworkStream = false; public boolean autoLoadSessionSettings = false; + public ThemeType themeType = ThemeType.DEFAULT; public GuiSettingsValues() { } @@ -87,6 +88,8 @@ class GuiSettings { saveToFile(); } + // Apply persisted theme + applyThemeFromSettings(); return true; } catch (IOException e) { @@ -168,6 +171,7 @@ class GuiSettings { topNav.configSelector.toggleAutoStartDataStreamFrontEnd(getAutoStartDataStream()); topNav.configSelector.toggleAutoStartNetworkStreamFrontEnd(getAutoStartNetworkStream()); topNav.configSelector.toggleAutoLoadSessionSettingsFrontEnd(getAutoLoadSessionSettings()); + applyThemeFromSettings(); } public void setExpertMode(ExpertModeEnum val) { @@ -241,4 +245,25 @@ class GuiSettings { values.autoLoadSessionSettings = b; saveToFile(); } + + public void setThemeType(ThemeType themeType) { + values.themeType = themeType; + saveToFile(); + println("OpenBCI_GUI::Settings: Theme saved as " + themeType.getString()); + } + + public ThemeType getThemeType() { + return values.themeType; + } + + private void applyThemeFromSettings() { + if (style != null && values.themeType != null) { + style.setTheme(values.themeType); + // If widgets are initialized, notify them of the theme + if (widgetManager != null) { + style.notifyThemeChange(); + } + println("OpenBCI_GUI::Settings: Applied persisted theme: " + values.themeType.getString()); + } + } } \ No newline at end of file diff --git a/OpenBCI_GUI/Style.pde b/OpenBCI_GUI/Style.pde index 52a5d3d8a..445d67439 100644 --- a/OpenBCI_GUI/Style.pde +++ b/OpenBCI_GUI/Style.pde @@ -3,17 +3,48 @@ // Style.pde - Theme Manager for OpenBCI GUI // // Created for GUI v7 Dark Mode Feature (Issue #705) +// Based on PR #1063 (Andrey1994) and PR #1248 (retiutut) architecture. // This class centralizes all color definitions and allows switching between themes. // // Themes available: -// - THEME_DEFAULT: Original OpenBCI blue theme -// - THEME_DARK: Dark mode for accessibility (reduced eye strain) +// - DEFAULT: Original OpenBCI blue theme (Legacy) +// - LIGHT: Light/soft theme with reduced contrast +// - DARK: Dark mode for accessibility (reduced eye strain) // /////////////////////////////////////////////////////////////////////////////////////// -// Theme constants - these identify which theme is active -final int THEME_DEFAULT = 0; // Original OpenBCI blue -final int THEME_DARK = 1; // Dark mode +/** + * Enum to store all possible themes. + * Based on PR #1063 ThemeType pattern. + */ +public enum ThemeType implements IndexingInterface { + DEFAULT(0, "Default"), + LIGHT(1, "Light"), + DARK(2, "Dark"); + + private int index; + private String label; + + ThemeType(int index, String label) { + this.index = index; + this.label = label; + } + + @Override + public String getString() { + return label; + } + + @Override + public int getIndex() { + return index; + } + + public ThemeType next() { + ThemeType[] vals = values(); + return vals[(ordinal() + 1) % vals.length]; + } +} // Global style instance - use this throughout the GUI Style style; @@ -33,13 +64,13 @@ Style style; class Style { // Current active theme - private int currentTheme; + private ThemeType currentTheme; // ============================================================ // COLOR DEFINITIONS FOR EACH THEME // ============================================================ - // --- THEME_DEFAULT (Original OpenBCI Blue) --- + // --- DEFAULT Theme (Original OpenBCI Blue / Legacy) --- private final color DEFAULT_TOPNAV_BG = color(31, 69, 110); // OPENBCI_BLUE private final color DEFAULT_SUBNAV_BG = color(57, 128, 204); // buttonsLightBlue private final color DEFAULT_BOX_BG = color(200); // boxColor @@ -55,8 +86,23 @@ class Style { private final color DEFAULT_HELP_WIDGET_BG = color(31, 69, 110); // OPENBCI_BLUE private final color DEFAULT_HELP_WIDGET_TEXT_BG = color(1, 18, 41); - // --- THEME_DARK (Dark Mode - OpenBCI colors but darker, near black) --- - // Based on OpenBCI blue (31, 69, 110) but much darker + // --- LIGHT Theme (Soft/reduced contrast) --- + private final color LIGHT_TOPNAV_BG = color(55, 95, 140); // Softer OpenBCI blue + private final color LIGHT_SUBNAV_BG = color(80, 145, 215); // Lighter blue + private final color LIGHT_BOX_BG = color(227, 230, 232); // #E3E6E8 soft grey + private final color LIGHT_BOX_STROKE = color(193, 200, 205); // #C1C8CD light grey border + private final color LIGHT_WIDGET_BG = color(240, 242, 245); // Near-white with slight warmth + private final color LIGHT_TEXT_PRIMARY = color(50, 60, 70); // Dark grey (not harsh black) + private final color LIGHT_TEXT_SECONDARY = color(120, 131, 140); // #78838C muted grey + private final color LIGHT_TEXT_ON_DARK = color(255); // WHITE + private final color LIGHT_BUTTON_BG = color(0, 163, 221); // #00A3DD cyan-blue + private final color LIGHT_BUTTON_TEXT = color(255); // WHITE + private final color LIGHT_BUTTON_HOVER = color(255, 148, 68); // #FF9444 orange hover + private final color LIGHT_BUTTON_PRESSED = color(249, 128, 37); // #F98025 orange pressed + private final color LIGHT_HELP_WIDGET_BG = color(55, 95, 140); // Match topnav + private final color LIGHT_HELP_WIDGET_TEXT_BG = color(30, 55, 85); + + // --- DARK Theme (Dark Mode - OpenBCI colors but darker, near black) --- private final color DARK_TOPNAV_BG = color(8, 18, 28); // Very dark OpenBCI blue (near black) private final color DARK_SUBNAV_BG = color(12, 28, 45); // Slightly lighter dark blue private final color DARK_BOX_BG = color(18, 35, 55); // Dark blue-grey for boxes @@ -83,11 +129,10 @@ class Style { // ============================================================ Style() { - // Default to the original OpenBCI theme - this.currentTheme = THEME_DEFAULT; + this.currentTheme = ThemeType.DEFAULT; } - Style(int theme) { + Style(ThemeType theme) { this.currentTheme = theme; } @@ -97,19 +142,17 @@ class Style { /** * Set the current theme - * @param theme Use THEME_DEFAULT or THEME_DARK + * @param theme ThemeType enum value */ - void setTheme(int theme) { - if (theme >= THEME_DEFAULT && theme <= THEME_DARK) { - this.currentTheme = theme; - println("Style: Theme changed to " + getThemeName()); - } + void setTheme(ThemeType theme) { + this.currentTheme = theme; + println("Style: Theme changed to " + getThemeName()); } /** - * Get the current theme ID + * Get the current ThemeType */ - int getTheme() { + ThemeType getThemeType() { return this.currentTheme; } @@ -117,20 +160,19 @@ class Style { * Get the current theme name as a string */ String getThemeName() { - switch(currentTheme) { - case THEME_DARK: - return "Dark"; - default: - return "Default"; - } + return currentTheme.getString(); } /** - * Cycle to the next theme (toggle between Default and Dark) + * Cycle to the next theme and persist the choice */ void cycleTheme() { - currentTheme = (currentTheme + 1) % 2; // Only 2 themes now + currentTheme = currentTheme.next(); println("Style: Theme cycled to " + getThemeName()); + // Persist the theme choice + if (guiSettings != null) { + guiSettings.setThemeType(currentTheme); + } notifyThemeChange(); } @@ -151,18 +193,28 @@ class Style { * Update the global dropdown colors based on current theme */ void updateDropdownColors() { - if (isDarkMode()) { - dropdownColorsGlobal.setActive((int)color(15, 35, 60)); // bg when pressed - dropdownColorsGlobal.setForeground((int)color(25, 50, 80)); // hover - dropdownColorsGlobal.setBackground((int)color(18, 35, 55)); // bg of boxes - dropdownColorsGlobal.setCaptionLabel((int)color(200, 210, 220)); // text in primary - dropdownColorsGlobal.setValueLabel((int)color(180, 190, 200)); // text in dropdowns - } else { - dropdownColorsGlobal.setActive((int)BUTTON_PRESSED); - dropdownColorsGlobal.setForeground((int)BUTTON_HOVER); - dropdownColorsGlobal.setBackground((int)color(255)); - dropdownColorsGlobal.setCaptionLabel((int)color(1, 18, 41)); - dropdownColorsGlobal.setValueLabel((int)color(100)); + switch(currentTheme) { + case DARK: + dropdownColorsGlobal.setActive((int)color(15, 35, 60)); + dropdownColorsGlobal.setForeground((int)color(25, 50, 80)); + dropdownColorsGlobal.setBackground((int)color(18, 35, 55)); + dropdownColorsGlobal.setCaptionLabel((int)color(200, 210, 220)); + dropdownColorsGlobal.setValueLabel((int)color(180, 190, 200)); + break; + case LIGHT: + dropdownColorsGlobal.setActive((int)LIGHT_BUTTON_PRESSED); + dropdownColorsGlobal.setForeground((int)LIGHT_BUTTON_HOVER); + dropdownColorsGlobal.setBackground((int)color(227, 230, 232)); + dropdownColorsGlobal.setCaptionLabel((int)color(50, 60, 70)); + dropdownColorsGlobal.setValueLabel((int)color(120, 131, 140)); + break; + default: // DEFAULT + dropdownColorsGlobal.setActive((int)BUTTON_PRESSED); + dropdownColorsGlobal.setForeground((int)BUTTON_HOVER); + dropdownColorsGlobal.setBackground((int)color(255)); + dropdownColorsGlobal.setCaptionLabel((int)color(1, 18, 41)); + dropdownColorsGlobal.setValueLabel((int)color(100)); + break; } } @@ -170,7 +222,21 @@ class Style { * Check if dark mode is active */ boolean isDarkMode() { - return currentTheme == THEME_DARK; + return currentTheme == ThemeType.DARK; + } + + /** + * Check if light mode is active + */ + boolean isLightMode() { + return currentTheme == ThemeType.LIGHT; + } + + /** + * Check if default mode is active + */ + boolean isDefaultMode() { + return currentTheme == ThemeType.DEFAULT; } // ============================================================ @@ -181,94 +247,167 @@ class Style { // --- Navigation Colors --- color getTopNavBackground() { - return isDarkMode() ? DARK_TOPNAV_BG : DEFAULT_TOPNAV_BG; + switch(currentTheme) { + case DARK: return DARK_TOPNAV_BG; + case LIGHT: return LIGHT_TOPNAV_BG; + default: return DEFAULT_TOPNAV_BG; + } } color getSubNavBackground() { - return isDarkMode() ? DARK_SUBNAV_BG : DEFAULT_SUBNAV_BG; + switch(currentTheme) { + case DARK: return DARK_SUBNAV_BG; + case LIGHT: return LIGHT_SUBNAV_BG; + default: return DEFAULT_SUBNAV_BG; + } } // --- Box/Panel Colors --- color getBoxColor() { - return isDarkMode() ? DARK_BOX_BG : DEFAULT_BOX_BG; + switch(currentTheme) { + case DARK: return DARK_BOX_BG; + case LIGHT: return LIGHT_BOX_BG; + default: return DEFAULT_BOX_BG; + } } color getBoxStrokeColor() { - return isDarkMode() ? DARK_BOX_STROKE : DEFAULT_BOX_STROKE; + switch(currentTheme) { + case DARK: return DARK_BOX_STROKE; + case LIGHT: return LIGHT_BOX_STROKE; + default: return DEFAULT_BOX_STROKE; + } } // --- Widget Colors --- color getWidgetBackground() { - return isDarkMode() ? DARK_WIDGET_BG : DEFAULT_WIDGET_BG; + switch(currentTheme) { + case DARK: return DARK_WIDGET_BG; + case LIGHT: return LIGHT_WIDGET_BG; + default: return DEFAULT_WIDGET_BG; + } } // --- Text Colors --- color getTextColor() { - return isDarkMode() ? DARK_TEXT_PRIMARY : DEFAULT_TEXT_PRIMARY; + switch(currentTheme) { + case DARK: return DARK_TEXT_PRIMARY; + case LIGHT: return LIGHT_TEXT_PRIMARY; + default: return DEFAULT_TEXT_PRIMARY; + } } color getSecondaryTextColor() { - return isDarkMode() ? DARK_TEXT_SECONDARY : DEFAULT_TEXT_SECONDARY; + switch(currentTheme) { + case DARK: return DARK_TEXT_SECONDARY; + case LIGHT: return LIGHT_TEXT_SECONDARY; + default: return DEFAULT_TEXT_SECONDARY; + } } color getTextOnDarkBackground() { - return isDarkMode() ? DARK_TEXT_ON_DARK : DEFAULT_TEXT_ON_DARK; + switch(currentTheme) { + case DARK: return DARK_TEXT_ON_DARK; + case LIGHT: return LIGHT_TEXT_ON_DARK; + default: return DEFAULT_TEXT_ON_DARK; + } } // --- Button Colors --- color getButtonColor() { - return isDarkMode() ? DARK_BUTTON_BG : DEFAULT_BUTTON_BG; + switch(currentTheme) { + case DARK: return DARK_BUTTON_BG; + case LIGHT: return LIGHT_BUTTON_BG; + default: return DEFAULT_BUTTON_BG; + } } color getButtonTextColor() { - return isDarkMode() ? DARK_BUTTON_TEXT : DEFAULT_BUTTON_TEXT; + switch(currentTheme) { + case DARK: return DARK_BUTTON_TEXT; + case LIGHT: return LIGHT_BUTTON_TEXT; + default: return DEFAULT_BUTTON_TEXT; + } } color getButtonHoverColor() { - return isDarkMode() ? DARK_BUTTON_HOVER : DEFAULT_BUTTON_HOVER; + switch(currentTheme) { + case DARK: return DARK_BUTTON_HOVER; + case LIGHT: return LIGHT_BUTTON_HOVER; + default: return DEFAULT_BUTTON_HOVER; + } } color getButtonPressedColor() { - return isDarkMode() ? DARK_BUTTON_PRESSED : DEFAULT_BUTTON_PRESSED; + switch(currentTheme) { + case DARK: return DARK_BUTTON_PRESSED; + case LIGHT: return LIGHT_BUTTON_PRESSED; + default: return DEFAULT_BUTTON_PRESSED; + } } // --- Help Widget (Console) Colors --- color getHelpWidgetBackground() { - return isDarkMode() ? DARK_HELP_WIDGET_BG : DEFAULT_HELP_WIDGET_BG; + switch(currentTheme) { + case DARK: return DARK_HELP_WIDGET_BG; + case LIGHT: return LIGHT_HELP_WIDGET_BG; + default: return DEFAULT_HELP_WIDGET_BG; + } } color getHelpWidgetTextBackground() { - return isDarkMode() ? DARK_HELP_WIDGET_TEXT_BG : DEFAULT_HELP_WIDGET_TEXT_BG; + switch(currentTheme) { + case DARK: return DARK_HELP_WIDGET_TEXT_BG; + case LIGHT: return LIGHT_HELP_WIDGET_TEXT_BG; + default: return DEFAULT_HELP_WIDGET_TEXT_BG; + } } // --- Graph/Plot Colors --- color getGraphBackground() { - // The outer background of plots - return isDarkMode() ? DARK_WIDGET_BG : color(255); + switch(currentTheme) { + case DARK: return DARK_WIDGET_BG; + case LIGHT: return color(240, 242, 245); + default: return color(255); + } } color getGraphBoxBackground() { - // The inner box background of plots (where data is drawn) - return isDarkMode() ? color(12, 25, 40) : color(245); + switch(currentTheme) { + case DARK: return color(12, 25, 40); + case LIGHT: return color(250, 251, 252); + default: return color(245); + } } color getGraphGridColor() { - return isDarkMode() ? color(35, 55, 80) : color(210); + switch(currentTheme) { + case DARK: return color(35, 55, 80); + case LIGHT: return color(220, 225, 230); + default: return color(210); + } } color getGraphLineColor() { - return isDarkMode() ? color(45, 70, 100) : color(210); + switch(currentTheme) { + case DARK: return color(45, 70, 100); + case LIGHT: return color(200, 208, 215); + default: return color(210); + } } color getGraphAxisColor() { - // Axis text and lines - return isDarkMode() ? color(160, 175, 190) : OPENBCI_DARKBLUE; + switch(currentTheme) { + case DARK: return color(160, 175, 190); + case LIGHT: return color(80, 95, 110); + default: return OPENBCI_DARKBLUE; + } } // --- Special Colors (dimmed in dark mode for reduced contrast) --- diff --git a/OpenBCI_GUI/Widget.pde b/OpenBCI_GUI/Widget.pde index e08082ace..95f071b6b 100644 --- a/OpenBCI_GUI/Widget.pde +++ b/OpenBCI_GUI/Widget.pde @@ -68,9 +68,9 @@ class Widget { //draw nav bars and button bars pushStyle(); - fill(style.isDarkMode() ? style.getSubNavBackground() : color(150, 150, 150)); + fill(style.isDefaultMode() ? color(150, 150, 150) : style.getSubNavBackground()); rect(x0, y0, w0, navH); //top bar - fill(style.isDarkMode() ? style.getBoxColor() : color(200, 200, 200)); + fill(style.isDefaultMode() ? color(200, 200, 200) : style.getBoxColor()); rect(x0, y0+navH, w0, navH); //button bar popStyle(); } From 5352a8af63ff57ee7cf406a743766d457861891c Mon Sep 17 00:00:00 2001 From: anaya yorke Date: Tue, 17 Mar 2026 13:44:21 -0700 Subject: [PATCH 7/7] Fix dark mode colors for widget backgrounds and UI elements - W_FFT.pde: Use theme-aware color for button bar instead of hardcoded grey - W_BandPower.pde: Use theme-aware colors for button bar and histogram lines - W_DigitalRead.pde: Add theme-aware colors for dots, strokes, and text labels - W_EMG.pde: Use theme-aware colors for threshold circles and bar containers - W_EMGJoystick.pde: Use theme-aware colors for graph background and axis lines - W_PulseSensor.pde: Use theme-aware colors for graph background and text - W_Spectrogram.pde: Use theme-aware background color for spectrogram display --- OpenBCI_GUI/W_BandPower.pde | 9 +++++++-- OpenBCI_GUI/W_DigitalRead.pde | 27 ++++++++++++++++++++++----- OpenBCI_GUI/W_EMG.pde | 6 +++--- OpenBCI_GUI/W_EMGJoystick.pde | 6 +++--- OpenBCI_GUI/W_FFT.pde | 2 +- OpenBCI_GUI/W_PulseSensor.pde | 6 +++--- OpenBCI_GUI/W_Spectrogram.pde | 2 +- 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/OpenBCI_GUI/W_BandPower.pde b/OpenBCI_GUI/W_BandPower.pde index 35c5e137f..9d2a70d53 100644 --- a/OpenBCI_GUI/W_BandPower.pde +++ b/OpenBCI_GUI/W_BandPower.pde @@ -158,7 +158,7 @@ class W_BandPower extends WidgetWithSettings { bp_plot.endDraw(); //for this widget need to redraw the grey bar, bc the FFT plot covers it up... - fill(200, 200, 200); + fill(style.isDefaultMode() ? color(200, 200, 200) : style.getBoxColor()); rect(x, y - NAV_HEIGHT, w, NAV_HEIGHT); //button bar popStyle(); @@ -249,8 +249,9 @@ class W_BandPower extends WidgetWithSettings { private void updatePlotColors() { // Apply theme colors to the BandPower plot color axisColor = style.getGraphAxisColor(); + color boxBgColor = style.getGraphBoxBackground(); bp_plot.setBgColor(style.getGraphBackground()); - bp_plot.setBoxBgColor(style.getGraphBoxBackground()); + bp_plot.setBoxBgColor(boxBgColor); bp_plot.setBoxLineColor(style.getGraphLineColor()); bp_plot.setGridLineColor(style.getGraphGridColor()); bp_plot.getXAxis().setFontColor(axisColor); @@ -260,6 +261,10 @@ class W_BandPower extends WidgetWithSettings { bp_plot.getYAxis().setLineColor(axisColor); bp_plot.getYAxis().getAxisLabel().setFontColor(axisColor); bp_plot.getHistogram().setFontColor(axisColor); + // Update histogram line colors to match background + bp_plot.getHistogram().setLineColors(new color[]{ + boxBgColor, boxBgColor, boxBgColor, boxBgColor, boxBgColor + }); } @Override diff --git a/OpenBCI_GUI/W_DigitalRead.pde b/OpenBCI_GUI/W_DigitalRead.pde index 22128d38b..fc611030c 100644 --- a/OpenBCI_GUI/W_DigitalRead.pde +++ b/OpenBCI_GUI/W_DigitalRead.pde @@ -103,6 +103,14 @@ class W_DigitalRead extends Widget { } } } + + @Override + public void updateColors() { + super.updateColors(); + for (int i = 0; i < numDigitalReadDots; i++) { + digitalReadDots[i].updateColors(); + } + } public void screenResized() { super.screenResized(); @@ -195,8 +203,13 @@ class DigitalReadDot{ color dotStroke = #d2d2d2; color dot0Fill = #f5f5f5; color dot1Fill = #f5f5f5; - color val0Fill = OPENBCI_DARKBLUE; // Note: This is used for digital indicator, may need dynamic update + color val0Fill = OPENBCI_DARKBLUE; color val1Fill = WHITE; + + color getDotStroke() { return style.isDarkMode() ? style.getGraphLineColor() : dotStroke; } + color getDot0Fill() { return style.isDarkMode() ? style.getGraphBoxBackground() : dot0Fill; } + color getVal0Fill() { return style.isDarkMode() ? style.getSecondaryTextColor() : val0Fill; } + color getPinTextColor() { return style.isDarkMode() ? style.getSecondaryTextColor() : OPENBCI_DARKBLUE; } int dotX; int dotY; @@ -239,9 +252,13 @@ class DigitalReadDot{ drawDigitalValue = true; digitalPin = new TextBox("D" + digitalInputString, dotX, dotY - dotWidth); - digitalPin.textColor = OPENBCI_DARKBLUE; + digitalPin.textColor = getPinTextColor(); digitalPin.alignH = CENTER; } + + void updateColors() { + digitalPin.textColor = getPinTextColor(); + } void update() { List lastData = digitalBoard.getDataWithDigital(1); @@ -272,10 +289,10 @@ class DigitalReadDot{ fill(dot1Fill); digitalValue.textColor = val1Fill; } else { - fill(dot0Fill); - digitalValue.textColor = val0Fill; + fill(getDot0Fill()); + digitalValue.textColor = getVal0Fill(); } - stroke(dotStroke); + stroke(getDotStroke()); ellipse(dotX, dotY, dotWidth, dotHeight); if (drawDigitalValue) { diff --git a/OpenBCI_GUI/W_EMG.pde b/OpenBCI_GUI/W_EMG.pde index a5538f094..67d61d32a 100644 --- a/OpenBCI_GUI/W_EMG.pde +++ b/OpenBCI_GUI/W_EMG.pde @@ -140,11 +140,11 @@ class W_Emg extends WidgetWithSettings { //circle for outer threshold noFill(); strokeWeight(1); - stroke(OPENBCI_DARKBLUE, 150); + stroke(style.isDarkMode() ? style.getSecondaryTextColor() : OPENBCI_DARKBLUE, 150); circle(2*colOffset/8, rowOffset / 2, scaleFactor * emgSettingsValues.getUpperThreshold(channel)); //circle for inner threshold - stroke(OPENBCI_DARKBLUE, 150); + stroke(style.isDarkMode() ? style.getSecondaryTextColor() : OPENBCI_DARKBLUE, 150); circle(2*colOffset/8, rowOffset / 2, scaleFactor * emgSettingsValues.getLowerThreshold(channel)); int _x = int(5*colOffset/8); @@ -159,7 +159,7 @@ class W_Emg extends WidgetWithSettings { //draw background bar container for mapped uV value indication strokeWeight(1); - stroke(OPENBCI_DARKBLUE, 150); + stroke(style.isDarkMode() ? style.getSecondaryTextColor() : OPENBCI_DARKBLUE, 150); noFill(); rect(_x, _y, _w, _h); diff --git a/OpenBCI_GUI/W_EMGJoystick.pde b/OpenBCI_GUI/W_EMGJoystick.pde index 8302954f8..feb29383a 100644 --- a/OpenBCI_GUI/W_EMGJoystick.pde +++ b/OpenBCI_GUI/W_EMGJoystick.pde @@ -181,12 +181,12 @@ class W_EmgJoystick extends WidgetWithSettings { */ //Background for graph - fill(graphBG); - stroke(graphStroke); + fill(style.isDarkMode() ? style.getGraphBoxBackground() : graphBG); + stroke(style.isDarkMode() ? style.getGraphLineColor() : graphStroke); circle(polarWindowX, polarWindowY, polarWindowDiameter); //X and Y axis lines - stroke(180); + stroke(style.isDarkMode() ? style.getGraphGridColor() : 180); line(polarWindowX - polarWindowHalfDiameter, polarWindowY, polarWindowX + polarWindowHalfDiameter, polarWindowY); line(polarWindowX, polarWindowY - polarWindowHalfDiameter, polarWindowX, polarWindowY + polarWindowHalfDiameter); diff --git a/OpenBCI_GUI/W_FFT.pde b/OpenBCI_GUI/W_FFT.pde index 8f539f583..2895e4778 100644 --- a/OpenBCI_GUI/W_FFT.pde +++ b/OpenBCI_GUI/W_FFT.pde @@ -173,7 +173,7 @@ class W_Fft extends WidgetWithSettings { fftPlot.endDraw(); //for this widget need to redraw the grey bar, bc the FFT plot covers it up... - fill(200, 200, 200); + fill(style.isDefaultMode() ? color(200, 200, 200) : style.getBoxColor()); rect(x, y - NAV_HEIGHT, w, NAV_HEIGHT); //button bar popStyle(); diff --git a/OpenBCI_GUI/W_PulseSensor.pde b/OpenBCI_GUI/W_PulseSensor.pde index f8344b07c..48a218c7b 100644 --- a/OpenBCI_GUI/W_PulseSensor.pde +++ b/OpenBCI_GUI/W_PulseSensor.pde @@ -108,12 +108,12 @@ class W_PulseSensor extends Widget { //remember to refer to x,y,w,h which are the positioning variables of the Widget class pushStyle(); - fill(graphBG); - stroke(graphStroke); + fill(style.isDarkMode() ? style.getGraphBoxBackground() : graphBG); + stroke(style.isDarkMode() ? style.getGraphLineColor() : graphStroke); rect(pulseWindowX,pulseWindowY,pulseWindowWidth,pulseWindowHeight); rect(bpmWindowX,bpmWindowY,bpmWindowWidth,bpmWindowHeight); - fill(50); + fill(style.isDarkMode() ? style.getSecondaryTextColor() : 50); textFont(p4, 16); textAlign(LEFT,CENTER); text("BPM "+bpmValue, bpmPositionX, bpmPositionY); diff --git a/OpenBCI_GUI/W_Spectrogram.pde b/OpenBCI_GUI/W_Spectrogram.pde index 83ab17302..2e2255ac3 100644 --- a/OpenBCI_GUI/W_Spectrogram.pde +++ b/OpenBCI_GUI/W_Spectrogram.pde @@ -164,7 +164,7 @@ class W_Spectrogram extends WidgetWithSettings { private void drawBackground() { pushStyle(); - fill(0); + fill(style.isDarkMode() ? style.getGraphBoxBackground() : 0); rect(x, y, w, h); popStyle(); }