Metadata-Version: 2.4
Name: pyside6-modern-widgets
Version: 0.5.1
Summary: Cross-platform modern frameless windows, navigation, and tabs for PySide6
Author: pyside6-modern-widgets contributors
License-Expression: MIT
Project-URL: Repository, https://github.com/zero-ljz/pyside6-modern-widgets
Project-URL: Changelog, https://github.com/zero-ljz/pyside6-modern-widgets/blob/main/CHANGELOG.md
Classifier: Development Status :: 3 - Alpha
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: User Interfaces
Classifier: Typing :: Typed
Requires-Python: <3.13,>=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: THIRD_PARTY_NOTICES.md
Requires-Dist: PySide6==6.8.3
Provides-Extra: dev
Requires-Dist: mypy>=1.13; extra == "dev"
Requires-Dist: pyinstaller>=6.11; extra == "dev"
Requires-Dist: pytest>=8.3; extra == "dev"
Requires-Dist: ruff>=0.9; extra == "dev"
Requires-Dist: types-pyinstaller>=6.11; extra == "dev"
Dynamic: license-file

# pyside6-modern-widgets

Cross-platform desktop widgets for PySide6. The package provides frameless
window chrome, navigation, and tabs while retaining familiar Qt widget APIs.

- `ModernWindow`: a frameless replacement for top-level `QWidget` windows with
  selected `QMainWindow`-compatible methods.
- `ModernDialog`: a frameless `QDialog` that preserves the standard dialog API.
- `ModernMessageBox`: a themed message box with familiar `QMessageBox` buttons
  and convenience methods.
- `ModernMenu`: a native `QMenu` with Windows 11 system acrylic (and an
  opaque fallback elsewhere) plus rounded outer and selected-item
  backgrounds.
- `ModernMenuBar`: a `QMenuBar` that creates `ModernMenu` drop-down menus.
- `NavigationSidebar`: a collapsible navigation sidebar.
- `NavigationView`: a sidebar and synchronized page stack in one widget.
- `TabView`: a WinUI-inspired tab widget.

## Supported environment

Supports Windows, macOS, and Linux with Python 3.10-3.12, PySide6 6.8.3, and
the Fusion style. Window backgrounds, including the custom title bar, use the
same Qt-painted, wallpaper-colored theme behavior on every platform.

On Windows, the custom chrome retains native activation, moving, resizing,
minimize/maximize/restore transitions, Aero Snap, shadows, and the system menu.
Windows 11 additionally provides DWM-rounded corners and Snap Layouts from the
custom maximize button; Windows 10 uses an opaque square-corner surface. On
platforms without equivalent frameless-window APIs, Qt supplies system moving,
resizing, and a menu with the available window commands.

## Installation

```shell
pip install pyside6-modern-widgets
```

## Upgrading to 0.5.0

Version 0.5.0 adds modern dialogs, message boxes, menus, and menu bars, plus
independent title-bar text/icon visibility and centered title text. It also
improves Windows maximize/restore behavior and content-aware navigation layout.
See the [changelog](CHANGELOG.md#050---2026-09-11) for the full release notes.

When upgrading from 0.4.x, remove uses of `WatercolorStyle`,
`theme_with_watercolor_style`, `ORIGINAL_LIGHT_THEME`, and `ORIGINAL_DARK_THEME`.
These exports and the title-bar Theme Style submenu have been removed. Widgets
now follow desktop-wallpaper colors by default; `LIGHT_THEME`, `DARK_THEME`,
`ModernTheme`, and `setTheme()` remain available for explicit theme overrides.

Automatic navigation overlay thresholds now depend on the current page's
minimum width instead of fixed window widths. Use `setAutoSidebarOverlay(False)`
and `setSidebarOverlay()` if your application needs explicit control.

## PyInstaller

The installed package automatically registers its PyInstaller hook. Applications
using these widgets can be frozen normally without package-specific
`--hidden-import` or `--add-data` options:

```shell
pyinstaller your_app.py
```

## Example

```python
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QApplication, QLabel

from pyside6_modern_widgets import ModernWindow

app = QApplication([])
window = ModernWindow()
window.setWindowTitle("Modern window")

file_menu = window.menuBar().addMenu("&File")
exit_action = QAction("Exit", window)
exit_action.triggered.connect(window.close)
file_menu.addAction(exit_action)

window.setCentralWidget(QLabel("Hello"))
window.resize(800, 500)
window.show()
app.exec()
```

`ModernWindow.menuBar()` returns a `ModernMenuBar`, so menus created from a
title or icon automatically use `ModernMenu`, including nested submenus.
`ModernMenuBar` itself provides a transparent background and rounded selection
highlight, including when constructed manually. It follows its containing
modern window's theme, or the global theme when used on its own.
When space is limited, its overflow button also opens a `ModernMenu` with the
same rounded surface and selection styling as the regular drop-down menus.

To place a manually created menu bar in the title bar's left control area:

```python
from pyside6_modern_widgets import ModernMenuBar

menu_bar = ModernMenuBar(window)
menu_bar.setNativeMenuBar(False)
menu_bar.addMenu("&File").addAction("Open")
window.titleBar.addCustomWidget(menu_bar, align="left")
window.setTitleVisible(False)
```

`setTitleVisible()` controls only title text. `setIconVisible()` independently
controls the title bar icon. Both default to `True` and preserve the actual
window title and icon used by the operating system. Updating either while it is
hidden does not show it again. `isTitleVisible()` and `isIconVisible()` return
the configured visibility, even when the window itself is hidden. An empty
window icon is not drawn, regardless of the icon visibility setting.

The icon stays at the far left whenever it is visible. Title text is left-aligned
by default, between the icon and left custom widgets such as menus. Use
`setTitleAlignment("center")` to center only the text on the window, with menus
following the icon, or `setTitleAlignment("left")` to restore the default order.
`titleAlignment()` returns the selected mode. In narrow windows, the centered
text stays within the space between the left controls and the window buttons.
Blank space remains available for dragging. Right custom widgets appear before
the window buttons. All six methods are also available on `window.titleBar`.

An existing top-level `QWidget` subclass can keep its direct layout when its
base class changes to `ModernWindow`. The standard `QWidget(parent, f)`
constructor shape and window flags are preserved:

```python
from PySide6.QtCore import Qt
from PySide6.QtWidgets import QLabel, QVBoxLayout

from pyside6_modern_widgets import ModernWindow


class ToolWindow(ModernWindow):
    def __init__(self, parent=None):
        super().__init__(parent, Qt.WindowType.Tool)
        layout = QVBoxLayout(self)
        layout.addWidget(QLabel("Tool content"))
```

Use either a layout installed directly on `ModernWindow` or its optional
`menuBar()`, `addToolBar()`, `statusBar()`, and `setCentralWidget()` compatibility
APIs. The two layout models intentionally cannot be mixed in one window.

`ModernDialog` accepts ordinary Qt layouts directly and retains `exec()`,
`accept()`, `reject()`, and the standard dialog result codes:

```python
from PySide6.QtWidgets import QDialogButtonBox, QLabel, QVBoxLayout

from pyside6_modern_widgets import ModernDialog

dialog = ModernDialog(window)
dialog.setWindowTitle("Settings")
layout = QVBoxLayout(dialog)
layout.addWidget(QLabel("Dialog content"))
buttons = QDialogButtonBox(
    QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
)
buttons.accepted.connect(dialog.accept)
buttons.rejected.connect(dialog.reject)
layout.addWidget(buttons)
dialog.exec()
```

`ModernMessageBox` provides the common information, question, warning, and
critical flows while returning `QMessageBox`-compatible standard buttons:

```python
from pyside6_modern_widgets import ModernMessageBox

answer = ModernMessageBox.question(
    window,
    "Confirm",
    "Continue with this operation?",
    ModernMessageBox.StandardButton.Yes | ModernMessageBox.StandardButton.No,
    ModernMessageBox.StandardButton.No,
)
```

`ModernMenu` accepts the same common constructor forms as `QMenu` and works
with ordinary `QAction` instances, separators, checkable actions, and submenus:

```python
from PySide6.QtGui import QAction

from pyside6_modern_widgets import ModernMenu

menu = ModernMenu("Actions", window)
menu.addAction(QAction("Open", menu))
menu.addSeparator()
menu.addMenu("Recent")
```

## Themes

Widgets use a modern theme colored from the current desktop wallpaper through
the process-wide theme manager. Starting with 0.5.1, inactive windows replace the
wallpaper effect with a solid background: `#F3F3F3` for light themes, or the
theme's surface color for dark themes. The effect returns when activated again,
using a 250 ms linear fade in both directions.
Rapid focus changes continue smoothly from the current blend. This applies to
window and dialog backgrounds, including title bars, message boxes, and
navigation sidebar overlays.

Widgets following the global theme update
automatically after a wallpaper change. The watcher responds directly to
changes in the current image file and performs a lightweight path and metadata
check every second for wallpaper switches; image sampling only runs after
a change is detected.

```python
from pyside6_modern_widgets import theme_manager

theme_manager().setFollowsSystemTheme(True)
```

Following the system theme switches the readable semantic colors between light
and dark while retaining colors extracted from the wallpaper. If the wallpaper
cannot be read, the surface falls back to its built-in modern colors. Layout
metrics can be customized with `ModernMetrics` without modifying component
internals. `theme_manager().refreshWallpaperTheme()` remains available for an
immediate manual refresh when needed.

`TabView` uses the standard Qt argument order: `addTab(widget, text)` or
`addTab(widget, icon, text)`. The former reverse `(widget, text, icon)` order is
not supported.

The runnable navigation example includes interactive window, dialog, and message
box pages. A separate multi-tab example is also available in the
[`examples`](examples) directory.

`NavigationView` automatically uses an overlay sidebar when expanding it beside
the current page would compress the page below its minimum width. A small
hysteresis margin prevents repeated mode changes near that width. On return to
the side-by-side layout, it restores the expand/collapse intent last selected
with the sidebar toggle. Overlay mode starts with its sidebar collapsed.
Applications with a custom responsive policy can call
`setAutoSidebarOverlay(False)` and control the mode with `setSidebarOverlay()`.

`ModernWindow` intentionally remains based on `QWidget`, so it is suitable for
top-level primary and auxiliary windows. Its compatibility surface is limited to the common
`menuBar()`, `addToolBar()`, `statusBar()`, and `setCentralWidget()` methods; it does
not implement `QMainWindow` docking or state-management features.

The bundled window and navigation icons are provided by
[Icons8](https://icons8.com) and remain subject to the Icons8 license.
