User interface overview
Every pixel JBrowser draws is a Qt widget, most of them custom-painted in paintEvent with QPainter rather than
styled with style sheets. That is what gives JBrowser its Windows 11 look over a translucent backdrop.
The main window#
MainWindow (QMainWindow, translucent, custom frame)
└── RootWidget paints the backdrop wash (colour tint or incognito black)
├── Sidebar favourites, spaces, the space's cards, tools
└── right column
├── TitleBar navigation, the address pill, tools, window buttons
├── FavoritesBar the bookmarks bar (Ctrl+Shift+B)
└── SpaceStack one Canvas per space, sliding between them
└── Canvas → WebCard one card per tab: header, web view, info bars, find bar
└── Gallery the overlay grid of every card (Ctrl+Shift+G)
overlays: LazyToolbar · HotkeySheet · ToastManager · Onboarding
The constructor also creates the BrowserController, installs WindowHooks as ctx.hooks, registers every
command and installs the shortcuts, and connects the lifecycle signals to the cards.
The window frame#
The window has no native title bar but keeps every native behaviour. NativeFrame in
platform/win.py handles WM_NCCALCSIZE (the whole window is client area),
WM_NCHITTEST (resize edges, the caption, and HTMAXBUTTON over the maximise button so Windows 11 shows the Snap
Layouts flyout), rounded corners and the drop shadow. apply_backdrop() asks DWM for Mica, Mica Alt or Acrylic
(DWMWA_SYSTEMBACKDROP_TYPE); translucent pixels in the window then show the backdrop.
The window's event filter#
MainWindow.eventFilter() sees events for the whole application. It implements the behaviours that cut across
widgets: Alt + mouse wheel pans the canvas from anywhere, pointing at the left edge peeks the hidden sidebar, mouse
back/forward buttons navigate the card under the pointer, and clicking inside a card focuses it.
Alt + drag anywhere on a card moves it.
It also gives menus and tooltips native rounded corners.
How widgets talk#
- Read from
ctx.state,Tabmodels andctx.settings, and connect to their signals. - Act through
ui(theBrowserController) orctx.commands.run(id). Never call another widget. - Draw with the theme's tokens (
theme().c("text2"),theme().surface("card")), never hard-coded colours, and repaint ontheme().changed. - Animate with
motion()(motion).
Overlays#
Full-window modal layers derive from Overlay in ui/widgets.py: a scrim over the
window, a panel from panel_rect(), Escape and clicks outside close it, focus returns where it was. The Lazy Toolbar
and the shortcut sheet are overlays. Transient messages use ToastManager.show(text, glyph, duration_ms).
Reusable widgets#
ui/widgets.py holds the custom-painted controls: IconButton (with badge and
progress ring), ToggleSwitch, Spinner, ProgressLine, ElidedLabel, InfoBarWidget, Toast, helpers such
as menu_action(), and the colour-tint picker TintPicker. Icons are glyphs from the Segoe Fluent Icons font
(ui/icons.py): icon("settings") returns a QIcon whose colour follows the theme,
and draw_glyph() paints one directly.
Accessibility#
- Custom widgets set accessible names and descriptions (
setAccessibleName), and keyboard focus is always visible. - Everything is reachable from the keyboard; the command registry makes every action searchable.
- Fluid animations off removes every animation; on first run it follows the Windows "Animation effects" setting.
Read next#
Theming, the canvas and cards, the sidebar, the Lazy Toolbar, the Gallery.