Skip to content

feat: dark mode support for pyRevit WPF forms - #3582

Draft
romangolev wants to merge 3 commits into
developfrom
feat/forms-dark-mode
Draft

feat: dark mode support for pyRevit WPF forms#3582
romangolev wants to merge 3 commits into
developfrom
feat/forms-dark-mode

Conversation

@romangolev

Copy link
Copy Markdown
Member

Summary

  • Central Theme.xaml resource dictionary injects a semantic light/dark palette (resolved from Revit's active UITheme) into every WPFWindow/WPFPanel, and re-templates the standard WPF controls (Button, TextBox, ComboBox, ListBox/ListView, CheckBox, RadioButton, Expander, GroupBox, TabItem, ScrollBar, ContextMenu, MenuItem, ToolTip, Separator, Label) against it, so both first-party dialogs (Settings, Extensions, list pickers, etc.) and third-party extension forms/dockable panels pick up the theme automatically.
  • Native window title bars are re-skinned to match via a new pyRevitLabs.Common.DwmApi wrapper (DWMWA_USE_IMMERSIVE_DARK_MODE on any supported Windows version, exact caption/text colors on Windows 11 22000+), rather than replacing OS chrome with custom-drawn chrome — keeps drag/resize/snap/min/max/close fully native.
  • Live refresh: windows and dockable panes subscribe to Revit's ThemeChanged event and repaint in place when the user toggles Revit's theme, no reload required.
  • Existing XAML across forms/, pyRevitCore.extension, and pyRevitDevTools.extension sample panels was swept for hardcoded light-only colors (White/Black/DimGray/etc.) and switched to the new theme brushes.

Test plan

  • Toggle Revit's UI theme (Light/Dark) and confirm Settings, Extensions, and list-picker dialogs match
  • Confirm a dockable panel (e.g. pyRevitDevTools sample panels) recolors live when the theme is toggled while docked/visible
  • Confirm title bar drag/resize/snap/minimize/maximize/close still work natively
  • Spot-check Revit 2023 or earlier (pre-UITheme) still renders the light palette with no errors

Add a centralized Theme.xaml resource dictionary and inject a semantic
light/dark palette into every WPF control's resources at construction,
selected from Revit's active UITheme. Theme.xaml re-templates the
standard WPF controls (Button, TextBox, ComboBox, ListBox/ListView,
CheckBox, RadioButton, Expander, GroupBox, TabItem, ScrollBar,
ContextMenu, MenuItem, ToolTip, Separator, Label) against
pyRevit<*>Brush resources so descendants pick the active palette via
DynamicResource.

WPFWindow now also recolors the native OS title bar to match: a new
pyRevitLabs.Common.DwmApi wrapper toggles DWMWA_USE_IMMERSIVE_DARK_MODE
on every supported Windows version and additionally sets the exact
caption/text colors on Windows 11 22000+. Both work runs are
no-ops on older Windows. Title bar colors track live theme changes via
a new ThemeChanged subscription; existing form XAMLs were switched
from hardcoded White/Black/DimGray/etc. to those DynamicResource
brushes, and the duplicated DatePicker ControlTemplate in
GetValueWindow.xaml was dropped in favor of the new themed style.
WPFWindow defaults _live_refresh_root_colors to False so a live
ThemeChanged refresh does not clobber the literal transparent
Background that HUD-style overlays (CommandSwitchWindow, SearchPrompt,
...) declare in their own XAML. WPFPanel overrides it to True:
dockable panes stay open for the whole Revit session and never use the
transparent-HUD pattern, so they need the live refresh to actually
repaint their Background/Foreground.

apply_theme was a thin wrapper that just delegated to
setup_resources(set_root_colors=False); collapse it back into
_on_theme_refresh so the per-subclass _live_refresh_root_colors
attribute is the single source of truth.
@devloai

devloai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Unable to trigger custom agent "Code Reviewer". You have run out of credits 😔
Please upgrade your plan or buy additional credits from the subscription page.

@romangolev romangolev changed the title Dark mode support for pyRevit WPF forms feat: dark mode support for pyRevit WPF forms Aug 21, 2026
@jmcouffin
jmcouffin requested a balanced review from Copilot August 21, 2026 17:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds automatic light/dark theming to all pyRevit WPF forms. A new central Theme.xaml resource dictionary is merged into every WPFWindow/WPFPanel, and a semantic palette (resolved from Revit's active UITheme) is injected as pyRevit*Color/pyRevit*Brush resources by _WPFMixin.setup_resources. Built-in WPF controls are re-templated against those brushes, native title bars are re-skinned via a new DwmApi P/Invoke wrapper, and windows/panes subscribe to Revit's ThemeChanged event to repaint live. Existing first-party XAML is swept to replace hardcoded light-only colors with the new theme brushes.

Changes:

  • New Theme.xaml restyles standard WPF controls; _ipy.py resolves the palette, injects brushes, applies DWM title-bar colors, and wires live ThemeChanged refresh.
  • New pyRevitLabs.Common.DwmApi wrapper for immersive dark mode + caption/text colors.
  • First-party XAML (SelectFromList, GetValueWindow, Settings, Extensions, DevTools panels) swept from hardcoded colors to theme brushes.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pyrevitlib/pyrevit/forms/Theme.xaml New resource dictionary re-templating standard controls against the semantic palette
pyrevitlib/pyrevit/forms/_ipy.py Palette definition, resource injection, DWM title-bar theming, live ThemeChanged subscribe/refresh
pyrevitlib/pyrevit/forms/SelectFromList.xaml Removed light-only styles; icons bound to theme foreground/brushes
pyrevitlib/pyrevit/forms/GetValueWindow.xaml Dropped hardcoded DatePicker template in favor of theme brushes
dev/pyRevitLabs/pyRevitLabs.Common/Unmanaged.cs New DwmApi wrapper (immersive dark mode + caption/text colors)
extensions/.../SettingsWindow.xaml Swept hardcoded colors to theme brushes; styles BasedOn theme defaults
extensions/.../ExtensionsWindow.xaml Swept hardcoded colors; list/checkbox styles BasedOn theme defaults
extensions/pyRevitDevTools.extension/SamplePanel.xaml Removed Background="White"; styles BasedOn theme defaults
extensions/pyRevitDevTools.extension/DockableExample.xaml Removed hardcoded Background="White"

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pyrevitlib/pyrevit/forms/Theme.xaml Outdated
Comment on lines +157 to +163
<ContentPresenter x:Name="content"
IsHitTestVisible="False"
Margin="{TemplateBinding Padding}"
HorizontalAlignment="Left" VerticalAlignment="Center"
Content="{Binding SelectionBoxItem, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplate="{Binding SelectionBoxItemTemplate, RelativeSource={RelativeSource TemplatedParent}}"
ContentTemplateSelector="{Binding ItemTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"/>
Add the editable text box template part to ComboBox so the editor surface picks up the theme brushes, and replace the custom ListViewItem template with style triggers that bind directly to pyRevit theme brushes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants