mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 13:32:50 -05:00
initial structure refactor
This commit is contained in:
116
CLAUDE.md
116
CLAUDE.md
@@ -40,7 +40,7 @@ When asked to backup Memory Bank System files, you will copy the core context fi
|
||||
|
||||
This is a Quickshell-based desktop shell implementation with Material Design 3 dark theme. The shell provides a complete desktop environment experience with panels, widgets, and system integration services.
|
||||
|
||||
**Architecture**: Modular design with clean separation between UI components (Widgets), system services (Services), and shared utilities (Common).
|
||||
**Architecture**: Modular design with clean separation between UI components (Modules), system services (Services), and shared utilities (Common).
|
||||
|
||||
## Technology Stack
|
||||
|
||||
@@ -81,11 +81,19 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
│ ├── NetworkService.qml
|
||||
│ ├── BrightnessService.qml
|
||||
│ └── [9 total services]
|
||||
└── Widgets/ # UI components
|
||||
├── TopBar.qml
|
||||
├── AppLauncher.qml
|
||||
├── ControlCenterPopup.qml
|
||||
└── [18 total widgets]
|
||||
├── Modules/ # UI components
|
||||
│ ├── TopBar.qml
|
||||
│ ├── AppLauncher.qml
|
||||
│ ├── ControlCenterPopup.qml
|
||||
│ └── [18 total modules]
|
||||
└── Widgets/ # Reusable UI controls
|
||||
├── DankIcon.qml
|
||||
├── DankSlider.qml
|
||||
├── DankToggle.qml
|
||||
├── DankTabBar.qml
|
||||
├── DankGridView.qml
|
||||
├── DankListView.qml
|
||||
└── [6 total widgets]
|
||||
```
|
||||
|
||||
### Component Organization
|
||||
@@ -106,10 +114,18 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
- **Examples**: AudioService, NetworkService, BrightnessService, WeatherService
|
||||
- Services handle system commands, state management, and hardware integration
|
||||
|
||||
4. **Widgets/** - Reusable UI components
|
||||
4. **Modules/** - UI components
|
||||
- **Full-screen components**: AppLauncher, ClipboardHistory, ControlCenterPopup
|
||||
- **Panel components**: TopBar, SystemTrayWidget, NotificationPopup
|
||||
- **Reusable controls**: CustomSlider, WorkspaceSwitcher
|
||||
- **Layout components**: WorkspaceSwitcher
|
||||
|
||||
5. **Widgets/** - Reusable UI controls
|
||||
- **DankIcon**: Centralized icon component with Material Design font integration
|
||||
- **DankSlider**: Enhanced slider with animations and smart detection
|
||||
- **DankToggle**: Consistent toggle switch component
|
||||
- **DankTabBar**: Unified tab bar implementation
|
||||
- **DankGridView**: Reusable grid view with adaptive columns
|
||||
- **DankListView**: Reusable list view with configurable styling
|
||||
|
||||
### Key Architectural Patterns
|
||||
|
||||
@@ -146,7 +162,15 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
- **AppLauncher**: Full-featured app grid/list with 93+ applications, search, categories
|
||||
- **ClipboardHistory**: Complete clipboard management with cliphist integration
|
||||
- **TopBar**: Per-monitor panels with workspace switching, clock, system tray
|
||||
- **CustomSlider**: Reusable enhanced slider with animations and smart detection
|
||||
|
||||
#### Key Widgets
|
||||
|
||||
- **DankIcon**: Centralized icon component with automatic Material Design font detection
|
||||
- **DankSlider**: Enhanced slider with animations and smart detection
|
||||
- **DankToggle**: Consistent toggle switch component
|
||||
- **DankTabBar**: Unified tab bar implementation
|
||||
- **DankGridView**: Reusable grid view with adaptive columns
|
||||
- **DankListView**: Reusable list view with configurable styling
|
||||
|
||||
## Code Conventions
|
||||
|
||||
@@ -161,7 +185,7 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
|
||||
2. **Naming Conventions**:
|
||||
- **Services**: Use `Singleton` type with `id: root`
|
||||
- **Components**: Use descriptive names (e.g., `CustomSlider`, `TopBar`)
|
||||
- **Components**: Use descriptive names (e.g., `DankSlider`, `TopBar`)
|
||||
- **Properties**: camelCase for properties, PascalCase for types
|
||||
|
||||
3. **Null-Safe Operations**:
|
||||
@@ -204,14 +228,16 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import Quickshell.Io // For Process, FileView
|
||||
import "../Common" // For Theme, utilities
|
||||
import "../Services" // For service access
|
||||
import qs.Common // For Theme, utilities
|
||||
import qs.Services // For service access
|
||||
import qs.Widgets // For reusable widgets (DankIcon, etc.)
|
||||
```
|
||||
|
||||
2. **Service Dependencies**:
|
||||
- Services should NOT import other services
|
||||
- Widgets can import and use services via property bindings
|
||||
- Modules and Widgets can import and use services via property bindings
|
||||
- Use `Theme.propertyName` for consistent styling
|
||||
- Use `DankIcon { name: "icon_name" }` for all icons instead of manual Text components
|
||||
|
||||
### Component Development Patterns
|
||||
|
||||
@@ -220,8 +246,8 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
// In services - detect capabilities
|
||||
property bool brightnessAvailable: false
|
||||
|
||||
// In widgets - adapt UI accordingly
|
||||
CustomSlider {
|
||||
// In modules - adapt UI accordingly
|
||||
DankSlider {
|
||||
visible: BrightnessService.brightnessAvailable
|
||||
enabled: BrightnessService.brightnessAvailable
|
||||
value: BrightnessService.brightnessLevel
|
||||
@@ -229,13 +255,13 @@ shell.qml # Main entry point (minimal orchestration)
|
||||
```
|
||||
|
||||
2. **Reusable Components**:
|
||||
- Create reusable widgets for common patterns (like CustomSlider)
|
||||
- Create reusable widgets for common patterns (like DankSlider)
|
||||
- Use configurable properties for different use cases
|
||||
- Include proper signal handling with unique names (avoid `valueChanged`)
|
||||
|
||||
3. **Service Integration**:
|
||||
- Services expose properties and functions
|
||||
- Widgets bind to service properties for reactive updates
|
||||
- Modules and Widgets bind to service properties for reactive updates
|
||||
- Use service functions for actions: `ServiceName.performAction(value)`
|
||||
- **CRITICAL**: DO NOT create wrapper functions for everything - bind directly to underlying APIs when possible
|
||||
- Example: Use `BluetoothService.adapter.discovering = true` instead of `BluetoothService.startScan()`
|
||||
@@ -284,6 +310,30 @@ When modifying the shell:
|
||||
6. **Multi-monitor**: Verify behavior with multiple displays
|
||||
7. **Feature detection**: Test on systems with/without required tools
|
||||
|
||||
### Adding New Modules
|
||||
|
||||
1. **Create component**:
|
||||
```bash
|
||||
# Create new module file
|
||||
touch Modules/NewModule.qml
|
||||
```
|
||||
|
||||
2. **Follow module patterns**:
|
||||
- Use `Theme.propertyName` for styling
|
||||
- Import `qs.Common` and `qs.Services` as needed
|
||||
- Import `qs.Widgets` for reusable components
|
||||
- Bind to service properties for reactive updates
|
||||
- Consider per-screen vs global behavior
|
||||
- Use `DankIcon` for icons instead of manual Text components
|
||||
|
||||
3. **Integration in shell.qml**:
|
||||
```qml
|
||||
NewModule {
|
||||
id: newModule
|
||||
// Configure properties
|
||||
}
|
||||
```
|
||||
|
||||
### Adding New Widgets
|
||||
|
||||
1. **Create component**:
|
||||
@@ -294,17 +344,10 @@ When modifying the shell:
|
||||
|
||||
2. **Follow widget patterns**:
|
||||
- Use `Theme.propertyName` for styling
|
||||
- Import `"../Common"` and `"../Services"` as needed
|
||||
- Bind to service properties for reactive updates
|
||||
- Consider per-screen vs global behavior
|
||||
|
||||
3. **Integration in shell.qml**:
|
||||
```qml
|
||||
NewWidget {
|
||||
id: newWidget
|
||||
// Configure properties
|
||||
}
|
||||
```
|
||||
- Import `qs.Common` for theming
|
||||
- Focus on reusability and composition
|
||||
- Keep widgets simple and focused
|
||||
- Use `DankIcon` for icons instead of manual Text components
|
||||
|
||||
### Adding New Services
|
||||
|
||||
@@ -329,9 +372,9 @@ When modifying the shell:
|
||||
}
|
||||
```
|
||||
|
||||
2. **Use in widgets**:
|
||||
2. **Use in modules**:
|
||||
```qml
|
||||
// In widget files
|
||||
// In module files
|
||||
property alias serviceValue: NewService.currentValue
|
||||
|
||||
SomeControl {
|
||||
@@ -352,9 +395,20 @@ When modifying the shell:
|
||||
### Best Practices Summary
|
||||
|
||||
- **Modularity**: Keep components focused and independent
|
||||
- **Reusability**: Create reusable components for common patterns
|
||||
- **Reusability**: Create reusable components for common patterns using Widgets/
|
||||
- **Responsiveness**: Use property bindings for reactive UI
|
||||
- **Robustness**: Implement feature detection and graceful degradation
|
||||
- **Consistency**: Follow Material Design 3 principles via Theme singleton
|
||||
- **Performance**: Minimize expensive operations and use appropriate data structures
|
||||
- **Icon Management**: Use `DankIcon` for all icons instead of manual Text components
|
||||
- **Widget System**: Leverage existing widgets (DankSlider, DankToggle, etc.) for consistency
|
||||
- **NO WRAPPER HELL**: Avoid creating unnecessary wrapper functions - bind directly to underlying APIs for better reactivity and performance
|
||||
|
||||
### Common Widget Patterns
|
||||
|
||||
1. **Icons**: Always use `DankIcon { name: "icon_name" }` instead of `Text { font.family: Theme.iconFont }`
|
||||
2. **Sliders**: Use `DankSlider` for consistent styling and behavior
|
||||
3. **Toggles**: Use `DankToggle` for switches and checkboxes
|
||||
4. **Tab Bars**: Use `DankTabBar` for tabbed interfaces
|
||||
5. **Lists**: Use `DankListView` for scrollable lists
|
||||
6. **Grids**: Use `DankGridView` for grid layouts
|
||||
|
||||
@@ -8,8 +8,6 @@ import Quickshell.Io
|
||||
import qs.Services
|
||||
|
||||
Singleton {
|
||||
// This dependency forces re-evaluation when colorUpdateTrigger changes
|
||||
// Just check if matugen is available
|
||||
|
||||
id: root
|
||||
|
||||
@@ -22,47 +20,38 @@ Singleton {
|
||||
property var matugenColors: ({
|
||||
})
|
||||
property bool extractionRequested: false
|
||||
property int colorUpdateTrigger: 0 // Force property re-evaluation
|
||||
// ──────────────── wallpaper change monitor ────────────────
|
||||
property int colorUpdateTrigger: 0
|
||||
property string lastWallpaperTimestamp: ""
|
||||
// ──────────────── color properties (MD3) ────────────────
|
||||
property color primary: getMatugenColor("primary", "#42a5f5")
|
||||
property color secondary: getMatugenColor("secondary", "#8ab4f8")
|
||||
property color tertiary: getMatugenColor("tertiary", "#bb86fc")
|
||||
property color tertiaryContainer: getMatugenColor("tertiary_container", "#3700b3")
|
||||
property color error: getMatugenColor("error", "#cf6679")
|
||||
property color inversePrimary: getMatugenColor("inverse_primary", "#6200ea")
|
||||
// backgrounds
|
||||
property color bg: getMatugenColor("background", "#1a1c1e")
|
||||
property color surface: getMatugenColor("surface", "#1a1c1e")
|
||||
property color surfaceContainer: getMatugenColor("surface_container", "#1e2023")
|
||||
property color surfaceContainerHigh: getMatugenColor("surface_container_high", "#292b2f")
|
||||
property color surfaceVariant: getMatugenColor("surface_variant", "#44464f")
|
||||
// text
|
||||
property color surfaceText: getMatugenColor("on_background", "#e3e8ef")
|
||||
property color primaryText: getMatugenColor("on_primary", "#ffffff")
|
||||
property color surfaceVariantText: getMatugenColor("on_surface_variant", "#c4c7c5")
|
||||
// containers & misc
|
||||
property color primaryContainer: getMatugenColor("primary_container", "#1976d2")
|
||||
property color surfaceTint: getMatugenColor("surface_tint", "#8ab4f8")
|
||||
property color outline: getMatugenColor("outline", "#8e918f")
|
||||
// legacy aliases
|
||||
property color accentHi: primary
|
||||
property color accentLo: secondary
|
||||
|
||||
// ──────────────── basic state ────────────────
|
||||
signal colorsUpdated()
|
||||
|
||||
function onLightModeChanged() {
|
||||
// Force color properties to update when light mode changes
|
||||
if (matugenColors && Object.keys(matugenColors).length > 0) {
|
||||
console.log("Light mode changed - updating dynamic colors");
|
||||
colorUpdateTrigger++; // This will trigger re-evaluation of all color properties
|
||||
colorUpdateTrigger++;
|
||||
colorsUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────── public helper ────────────────
|
||||
function extractColors() {
|
||||
console.log("Colors.extractColors() called, matugenAvailable:", matugenAvailable);
|
||||
extractionRequested = true;
|
||||
@@ -73,9 +62,7 @@ Singleton {
|
||||
}
|
||||
|
||||
function getMatugenColor(path, fallback) {
|
||||
// Include colorUpdateTrigger in the function to make properties reactive to changes
|
||||
colorUpdateTrigger;
|
||||
// Use light or dark colors based on Theme.isLightMode
|
||||
const colorMode = (typeof Theme !== "undefined" && Theme.isLightMode) ? "light" : "dark";
|
||||
let cur = matugenColors && matugenColors.colors && matugenColors.colors[colorMode];
|
||||
for (const part of path.split(".")) {
|
||||
@@ -93,15 +80,11 @@ Singleton {
|
||||
|
||||
Component.onCompleted: {
|
||||
console.log("Colors.qml → home =", homeDir);
|
||||
// Don't automatically run color extraction - only when requested
|
||||
matugenCheck.running = true;
|
||||
// Connect to Theme light mode changes to update colors
|
||||
if (typeof Theme !== "undefined")
|
||||
Theme.isLightModeChanged.connect(root.onLightModeChanged);
|
||||
|
||||
}
|
||||
|
||||
// ──────────────── availability checks ────────────────
|
||||
Process {
|
||||
id: matugenCheck
|
||||
|
||||
@@ -115,7 +98,6 @@ Singleton {
|
||||
ToastService.showWarning("matugen not found - dynamic theming disabled");
|
||||
return ;
|
||||
}
|
||||
// If extraction was requested, continue the process
|
||||
if (extractionRequested) {
|
||||
console.log("Continuing with color extraction");
|
||||
fileChecker.running = true;
|
||||
@@ -124,7 +106,7 @@ Singleton {
|
||||
}
|
||||
|
||||
Process {
|
||||
id: fileChecker // exists & readable?
|
||||
id: fileChecker
|
||||
|
||||
command: ["test", "-r", wallpaperPath]
|
||||
onExited: (code) => {
|
||||
@@ -139,13 +121,11 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// ──────────────── matugen invocation ────────────────
|
||||
Process {
|
||||
id: matugenProcess
|
||||
|
||||
command: ["matugen", "-v", "image", wallpaperPath, "--json", "hex"]
|
||||
|
||||
// ── grab stdout as a stream ──
|
||||
stdout: StdioCollector {
|
||||
id: matugenCollector
|
||||
|
||||
@@ -162,7 +142,6 @@ Singleton {
|
||||
root.matugenColors = JSON.parse(out);
|
||||
root.colorsUpdated();
|
||||
ToastService.clearWallpaperError();
|
||||
ToastService.showInfo("Loaded Dynamic Theme Colors");
|
||||
} catch (e) {
|
||||
console.error("JSON parse failed:", e);
|
||||
ToastService.wallpaperErrorStatus = "error";
|
||||
@@ -171,7 +150,6 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
// grab stderr too, so we can print it above
|
||||
stderr: StdioCollector {
|
||||
id: matugenErr
|
||||
}
|
||||
|
||||
@@ -388,12 +388,6 @@ Singleton {
|
||||
// Transparency system - can be overridden by Prefs
|
||||
property real panelTransparency: 0.85
|
||||
property real popupTransparency: 0.92
|
||||
property string iconFont: { Qt.fontFamilies()
|
||||
.indexOf("Material Symbols Rounded") !== -1 ? "Material Symbols Rounded" : "Material Icons Round" }
|
||||
property string iconFontFilled: { Qt.fontFamilies()
|
||||
.indexOf("Material Symbols Rounded") !== -1 ? "Material Symbols Rounded" : "Material Icons Rounded" }
|
||||
property int iconFontWeight: Font.Normal
|
||||
property int iconFontFilledWeight: Font.Medium
|
||||
|
||||
// Handle successful color extraction
|
||||
function onColorsUpdated() {
|
||||
|
||||
@@ -7,6 +7,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
// For recents, use the recent apps from Prefs and filter out non-existent ones
|
||||
@@ -74,7 +75,7 @@ PanelWindow {
|
||||
if (filteredModel.count > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = appGrid.columnsCount || 8;
|
||||
var columnsCount = appGrid.columns || 4;
|
||||
selectedIndex = Math.min(selectedIndex + columnsCount, filteredModel.count - 1);
|
||||
} else {
|
||||
// List navigation: next item
|
||||
@@ -87,7 +88,7 @@ PanelWindow {
|
||||
if (filteredModel.count > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = appGrid.columnsCount || 8;
|
||||
var columnsCount = appGrid.columns || 4;
|
||||
selectedIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
} else {
|
||||
// List navigation: previous item
|
||||
@@ -459,13 +460,11 @@ PanelWindow {
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "search"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
name: "search"
|
||||
size: Theme.iconSize
|
||||
color: searchField.activeFocus ? Theme.primary : Theme.surfaceVariantText
|
||||
font.weight: Theme.iconFontWeight
|
||||
}
|
||||
|
||||
TextInput {
|
||||
@@ -526,11 +525,10 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: searchField.text.length > 0
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "close"
|
||||
size: 16
|
||||
color: clearSearchArea.containsMouse ? Theme.outline : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -581,10 +579,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "category"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
DankIcon {
|
||||
name: "category"
|
||||
size: 18
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -599,13 +596,12 @@ PanelWindow {
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: showCategories ? "expand_less" : "expand_more"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: showCategories ? "expand_less" : "expand_more"
|
||||
size: 18
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -635,11 +631,10 @@ PanelWindow {
|
||||
radius: Theme.cornerRadius
|
||||
color: viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : listViewArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "view_list"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 20
|
||||
name: "view_list"
|
||||
size: 20
|
||||
color: viewMode === "list" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -664,11 +659,10 @@ PanelWindow {
|
||||
radius: Theme.cornerRadius
|
||||
color: viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : gridViewArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "grid_view"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 20
|
||||
name: "grid_view"
|
||||
size: 20
|
||||
color: viewMode === "grid" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -705,96 +699,51 @@ PanelWindow {
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
// List view scroll container
|
||||
ScrollView {
|
||||
// List view
|
||||
DankListView {
|
||||
id: appList
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
visible: viewMode === "list"
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: appList
|
||||
|
||||
// Make mouse wheel scrolling more responsive
|
||||
property real wheelStepSize: 60
|
||||
|
||||
width: parent.width
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
model: filteredModel
|
||||
delegate: listDelegate
|
||||
currentIndex: selectedIndex
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120; // Standard wheel step
|
||||
appList.contentY -= steps * appList.wheelStepSize;
|
||||
// Ensure we stay within bounds
|
||||
if (appList.contentY < 0)
|
||||
appList.contentY = 0;
|
||||
else if (appList.contentY > appList.contentHeight - appList.height)
|
||||
appList.contentY = Math.max(0, appList.contentHeight - appList.height);
|
||||
}
|
||||
model: filteredModel
|
||||
currentIndex: selectedIndex
|
||||
itemHeight: 72
|
||||
iconSize: 56
|
||||
showDescription: true
|
||||
onItemClicked: function(index, modelData) {
|
||||
if (modelData.desktopEntry) {
|
||||
Prefs.addRecentApp(modelData.desktopEntry);
|
||||
modelData.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(modelData.exec);
|
||||
}
|
||||
|
||||
launcher.hide();
|
||||
}
|
||||
onItemHovered: function(index) {
|
||||
selectedIndex = index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Grid view scroll container
|
||||
ScrollView {
|
||||
// Grid view
|
||||
DankGridView {
|
||||
id: appGrid
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
visible: viewMode === "grid"
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
GridView {
|
||||
id: appGrid
|
||||
|
||||
// Responsive cell sizes based on screen width - 4 columns
|
||||
property int columnsCount: 4
|
||||
property int baseCellWidth: (width - Theme.spacingS * 2) / columnsCount
|
||||
property int baseCellHeight: baseCellWidth + 20
|
||||
// Center the grid content
|
||||
property int remainingSpace: width - (columnsCount * cellWidth)
|
||||
// Make mouse wheel scrolling more responsive
|
||||
property real wheelStepSize: 60
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
cellWidth: baseCellWidth
|
||||
cellHeight: baseCellHeight
|
||||
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
|
||||
rightMargin: leftMargin
|
||||
model: filteredModel
|
||||
delegate: gridDelegate
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120; // Standard wheel step
|
||||
appGrid.contentY -= steps * appGrid.wheelStepSize;
|
||||
// Ensure we stay within bounds
|
||||
if (appGrid.contentY < 0)
|
||||
appGrid.contentY = 0;
|
||||
else if (appGrid.contentY > appGrid.contentHeight - appGrid.height)
|
||||
appGrid.contentY = Math.max(0, appGrid.contentHeight - appGrid.height);
|
||||
}
|
||||
model: filteredModel
|
||||
columns: 4
|
||||
adaptiveColumns: false
|
||||
currentIndex: selectedIndex
|
||||
onItemClicked: function(index, modelData) {
|
||||
if (modelData.desktopEntry) {
|
||||
Prefs.addRecentApp(modelData.desktopEntry);
|
||||
modelData.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(modelData.exec);
|
||||
}
|
||||
|
||||
launcher.hide();
|
||||
}
|
||||
onItemHovered: function(index) {
|
||||
selectedIndex = index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -918,163 +867,6 @@ PanelWindow {
|
||||
|
||||
}
|
||||
|
||||
// List delegate with new loader
|
||||
Component {
|
||||
id: listDelegate
|
||||
|
||||
Rectangle {
|
||||
width: appList.width
|
||||
height: 72
|
||||
radius: Theme.cornerRadiusLarge
|
||||
color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : appMouseArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: ListView.isCurrentItem ? 2 : 1
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Item {
|
||||
width: 56
|
||||
height: 56
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Loader {
|
||||
id: listIconLoader
|
||||
|
||||
property var modelData: model
|
||||
|
||||
anchors.fill: parent
|
||||
sourceComponent: iconComponent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - 56 - Theme.spacingL
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: model.comment || "Application"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
visible: model.comment && model.comment.length > 0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: appMouseArea
|
||||
|
||||
property bool hovered: containsMouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: selectedIndex = index
|
||||
onClicked: {
|
||||
if (model.desktopEntry) {
|
||||
Prefs.addRecentApp(model.desktopEntry);
|
||||
model.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(model.exec);
|
||||
}
|
||||
launcher.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Grid delegate with new loader (uses dynamic icon size)
|
||||
Component {
|
||||
id: gridDelegate
|
||||
|
||||
Rectangle {
|
||||
width: appGrid.cellWidth - 8
|
||||
height: appGrid.cellHeight - 8
|
||||
radius: Theme.cornerRadiusLarge
|
||||
color: selectedIndex === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : gridAppArea.hovered ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: selectedIndex === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: selectedIndex === index ? 2 : 1
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
property int iconSize: Math.min(56, Math.max(32, appGrid.cellWidth * 0.6))
|
||||
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Loader {
|
||||
id: gridIconLoader
|
||||
|
||||
property var modelData: model
|
||||
|
||||
anchors.fill: parent
|
||||
sourceComponent: iconComponent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: appGrid.cellWidth - 12
|
||||
text: model.name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: gridAppArea
|
||||
|
||||
property bool hovered: containsMouse
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: selectedIndex = index
|
||||
onClicked: {
|
||||
if (model.desktopEntry) {
|
||||
Prefs.addRecentApp(model.desktopEntry);
|
||||
model.desktopEntry.execute();
|
||||
} else {
|
||||
launcher.launchApp(model.exec);
|
||||
}
|
||||
launcher.hide();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
@@ -105,11 +106,10 @@ PanelWindow {
|
||||
radius: 16
|
||||
color: closeBatteryArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
name: "close"
|
||||
size: Theme.iconSize - 4
|
||||
color: closeBatteryArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -143,10 +143,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Text {
|
||||
text: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
DankIcon {
|
||||
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
||||
size: Theme.iconSizeLarge
|
||||
color: {
|
||||
if (BatteryService.isLowBattery && !BatteryService.isCharging)
|
||||
return Theme.error;
|
||||
@@ -232,10 +231,9 @@ PanelWindow {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Text {
|
||||
text: Theme.getBatteryIcon(0, false, false)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 36
|
||||
DankIcon {
|
||||
name: Theme.getBatteryIcon(0, false, false)
|
||||
size: 36
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -364,10 +362,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: Theme.getPowerProfileIcon(modelData)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: Theme.getPowerProfileIcon(modelData)
|
||||
size: Theme.iconSize
|
||||
color: batteryControlPopup.isActiveProfile(modelData) ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -428,10 +425,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "warning"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "warning"
|
||||
size: Theme.iconSize
|
||||
color: Theme.error
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell.Services.UPower
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: batteryWidget
|
||||
@@ -20,10 +21,9 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
DankIcon {
|
||||
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
|
||||
size: Theme.iconSize - 6
|
||||
color: {
|
||||
if (!BatteryService.batteryAvailable)
|
||||
return Theme.surfaceText;
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Column {
|
||||
id: calendarWidget
|
||||
@@ -57,13 +58,11 @@ Column {
|
||||
radius: Theme.cornerRadius
|
||||
color: prevMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "chevron_left"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
name: "chevron_left"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
font.weight: Theme.iconFontWeight
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -98,13 +97,11 @@ Column {
|
||||
radius: Theme.cornerRadius
|
||||
color: nextMonthArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "chevron_right"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
name: "chevron_right"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
font.weight: Theme.iconFontWeight
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
// Events widget for selected date - Material Design 3 style
|
||||
Rectangle {
|
||||
@@ -67,10 +68,9 @@ Rectangle {
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "event"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
DankIcon {
|
||||
name: "event"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -91,10 +91,9 @@ Rectangle {
|
||||
spacing: Theme.spacingXS
|
||||
visible: !hasEvents
|
||||
|
||||
Text {
|
||||
text: "event_busy"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
DankIcon {
|
||||
name: "event_busy"
|
||||
size: Theme.iconSize + 8
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -202,10 +201,9 @@ Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: "schedule"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "schedule"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -239,10 +237,9 @@ Rectangle {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: modelData.location !== ""
|
||||
|
||||
Text {
|
||||
text: "location_on"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "location_on"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import Quickshell
|
||||
import Quickshell.Services.Mpris
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: mediaPlayerWidget
|
||||
@@ -90,10 +91,9 @@ Rectangle {
|
||||
spacing: Theme.spacingS
|
||||
visible: (!activePlayer && !lastValidTitle) || (activePlayer && activePlayer.trackTitle === "" && lastValidTitle === "")
|
||||
|
||||
Text {
|
||||
text: "music_note"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
DankIcon {
|
||||
name: "music_note"
|
||||
size: Theme.iconSize + 8
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -149,11 +149,10 @@ Rectangle {
|
||||
visible: albumArt.status !== Image.Ready
|
||||
color: "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "album"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 28
|
||||
name: "album"
|
||||
size: 28
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -356,11 +355,10 @@ Rectangle {
|
||||
radius: 14
|
||||
color: prevBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_previous"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "skip_previous"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -393,11 +391,10 @@ Rectangle {
|
||||
radius: 16
|
||||
color: Theme.primary
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 20
|
||||
name: activePlayer && activePlayer.playbackState === MprisPlaybackState.Playing ? "pause" : "play_arrow"
|
||||
size: 20
|
||||
color: Theme.background
|
||||
}
|
||||
|
||||
@@ -417,11 +414,10 @@ Rectangle {
|
||||
radius: 14
|
||||
color: nextBtnArea.containsMouse ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_next"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "skip_next"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick.Controls
|
||||
import QtQuick.Effects
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: weatherWidget
|
||||
@@ -21,10 +22,9 @@ Rectangle {
|
||||
spacing: Theme.spacingS
|
||||
visible: !WeatherService.weather.available || WeatherService.weather.temp === 0
|
||||
|
||||
Text {
|
||||
text: "cloud_off"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
DankIcon {
|
||||
name: "cloud_off"
|
||||
size: Theme.iconSize + 8
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -55,10 +55,9 @@ Rectangle {
|
||||
spacing: Theme.spacingL
|
||||
|
||||
// Weather icon
|
||||
Text {
|
||||
text: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
DankIcon {
|
||||
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -109,10 +108,9 @@ Rectangle {
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "humidity_low"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "humidity_low"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -129,10 +127,9 @@ Rectangle {
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "air"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "air"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -149,10 +146,9 @@ Rectangle {
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "wb_twilight"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "wb_twilight"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -169,10 +165,9 @@ Rectangle {
|
||||
Row {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "bedtime"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "bedtime"
|
||||
size: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import Quickshell.Services.Pipewire
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../Widgets"
|
||||
|
||||
Item {
|
||||
id: audioTab
|
||||
@@ -23,57 +25,22 @@ Item {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Audio Sub-tabs
|
||||
Row {
|
||||
DankTabBar {
|
||||
width: parent.width
|
||||
height: 40
|
||||
spacing: 2
|
||||
|
||||
Rectangle {
|
||||
width: parent.width / 2 - 1
|
||||
height: parent.height
|
||||
radius: Theme.cornerRadius
|
||||
color: audioTab.audioSubTab === 0 ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Output"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: audioTab.audioSubTab === 0 ? Theme.primaryText : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
tabHeight: 40
|
||||
currentIndex: audioTab.audioSubTab
|
||||
showIcons: false
|
||||
model: [
|
||||
{
|
||||
"text": "Output"
|
||||
},
|
||||
{
|
||||
"text": "Input"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: audioTab.audioSubTab = 0
|
||||
}
|
||||
|
||||
]
|
||||
onTabClicked: function(index) {
|
||||
audioTab.audioSubTab = index;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width / 2 - 1
|
||||
height: parent.height
|
||||
radius: Theme.cornerRadius
|
||||
color: audioTab.audioSubTab === 1 ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "Input"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: audioTab.audioSubTab === 1 ? Theme.primaryText : Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: audioTab.audioSubTab = 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Output Tab Content
|
||||
@@ -103,10 +70,9 @@ Item {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: audioTab.volumeMuted ? "volume_off" : "volume_down"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: audioTab.volumeMuted ? "volume_off" : "volume_down"
|
||||
size: Theme.iconSize
|
||||
color: audioTab.volumeMuted ? Theme.error : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -248,10 +214,9 @@ Item {
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "volume_up"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "volume_up"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -288,10 +253,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "check_circle"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: "check_circle"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
@@ -335,8 +299,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: {
|
||||
DankIcon {
|
||||
name: {
|
||||
if (modelData.name.includes("bluez"))
|
||||
return "headset";
|
||||
else if (modelData.name.includes("hdmi"))
|
||||
@@ -346,8 +310,7 @@ Item {
|
||||
else
|
||||
return "speaker";
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
size: Theme.iconSize
|
||||
color: modelData === AudioService.sink ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -429,10 +392,9 @@ Item {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: audioTab.micMuted ? "mic_off" : "mic"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: audioTab.micMuted ? "mic_off" : "mic"
|
||||
size: Theme.iconSize
|
||||
color: audioTab.micMuted ? Theme.error : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -574,10 +536,9 @@ Item {
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "mic"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "mic"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -614,10 +575,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "check_circle"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: "check_circle"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
}
|
||||
|
||||
@@ -661,8 +621,8 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: {
|
||||
DankIcon {
|
||||
name: {
|
||||
if (modelData.name.includes("bluez"))
|
||||
return "headset_mic";
|
||||
else if (modelData.name.includes("usb"))
|
||||
@@ -670,8 +630,7 @@ Item {
|
||||
else
|
||||
return "mic";
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
size: Theme.iconSize
|
||||
color: modelData === AudioService.source ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: bluetoothTab
|
||||
@@ -34,10 +35,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "bluetooth"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
DankIcon {
|
||||
name: "bluetooth"
|
||||
size: Theme.iconSizeLarge
|
||||
color: BluetoothService.adapter && BluetoothService.adapter.enabled ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -109,10 +109,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: BluetoothService.getDeviceIcon(modelData)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: BluetoothService.getDeviceIcon(modelData)
|
||||
size: Theme.iconSize
|
||||
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -169,11 +168,9 @@ Item {
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: "more_vert"
|
||||
font.family: Theme.iconFont
|
||||
font.weight: Theme.iconFontWeight
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "more_vert"
|
||||
size: Theme.iconSize
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.6
|
||||
anchors.centerIn: parent
|
||||
@@ -258,10 +255,9 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "bluetooth_searching"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -341,10 +337,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: BluetoothService.getDeviceIcon(modelData)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: BluetoothService.getDeviceIcon(modelData)
|
||||
size: Theme.iconSize
|
||||
color: {
|
||||
if (modelData.pairing)
|
||||
return Theme.warning;
|
||||
@@ -402,10 +397,9 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: BluetoothService.getSignalIcon(modelData)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: BluetoothService.getSignalIcon(modelData)
|
||||
size: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
||||
visible: modelData.signalStrength !== undefined && modelData.signalStrength > 0 && !modelData.pairing && !modelData.blocked
|
||||
}
|
||||
@@ -510,10 +504,9 @@ Item {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "sync"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
DankIcon {
|
||||
name: "sync"
|
||||
size: Theme.iconSizeLarge
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -638,10 +631,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: bluetoothContextMenuWindow.deviceData && bluetoothContextMenuWindow.deviceData.connected ? "link_off" : "link"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
DankIcon {
|
||||
name: bluetoothContextMenuWindow.deviceData && bluetoothContextMenuWindow.deviceData.connected ? "link_off" : "link"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -712,10 +704,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "delete"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
DankIcon {
|
||||
name: "delete"
|
||||
size: Theme.iconSize - 2
|
||||
color: forgetArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
opacity: 0.7
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -8,6 +8,8 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../Widgets"
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
@@ -225,22 +227,20 @@ PanelWindow {
|
||||
color: Theme.primary
|
||||
visible: !parent.hasImage
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "person"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
name: "person"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Error icon for when the image fails to load.
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "warning"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
name: "warning"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
visible: Prefs.profileImage !== "" && profileImageLoader.status === Image.Error
|
||||
}
|
||||
@@ -292,14 +292,13 @@ PanelWindow {
|
||||
color: "transparent"
|
||||
clip: true
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
name: root.powerOptionsExpanded ? "expand_less" : "power_settings_new"
|
||||
size: Theme.iconSize - 2
|
||||
color: powerButton.containsMouse || root.powerOptionsExpanded ? Theme.error : Theme.surfaceText
|
||||
|
||||
Behavior on text {
|
||||
Behavior on name {
|
||||
// Smooth icon transition
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
@@ -312,7 +311,7 @@ PanelWindow {
|
||||
|
||||
PropertyAction {
|
||||
target: parent
|
||||
property: "text"
|
||||
property: "name"
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
@@ -359,11 +358,10 @@ PanelWindow {
|
||||
radius: 20
|
||||
color: settingsButton.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.5)
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "settings"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
name: "settings"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -420,10 +418,9 @@ PanelWindow {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "logout"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "logout"
|
||||
size: Theme.fontSizeSmall
|
||||
color: logoutButton.containsMouse ? Theme.warning : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -476,10 +473,9 @@ PanelWindow {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "restart_alt"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "restart_alt"
|
||||
size: Theme.fontSizeSmall
|
||||
color: rebootButton.containsMouse ? Theme.warning : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -532,10 +528,9 @@ PanelWindow {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "power_settings_new"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
DankIcon {
|
||||
name: "power_settings_new"
|
||||
size: Theme.fontSizeSmall
|
||||
color: shutdownButton.containsMouse ? Theme.error : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -599,105 +594,54 @@ PanelWindow {
|
||||
}
|
||||
|
||||
// Tab buttons
|
||||
Row {
|
||||
DankTabBar {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
model: {
|
||||
tabHeight: 40
|
||||
currentIndex: {
|
||||
let tabs = ["network", "audio"];
|
||||
if (BluetoothService.available)
|
||||
tabs.push("bluetooth");
|
||||
tabs.push("display");
|
||||
return tabs.indexOf(root.currentTab);
|
||||
}
|
||||
model: {
|
||||
let tabs = [{
|
||||
"name": "Network",
|
||||
"text": "Network",
|
||||
"icon": "wifi",
|
||||
"id": "network",
|
||||
"available": true
|
||||
"id": "network"
|
||||
}];
|
||||
// Always show audio
|
||||
tabs.push({
|
||||
"name": "Audio",
|
||||
"text": "Audio",
|
||||
"icon": "volume_up",
|
||||
"id": "audio",
|
||||
"available": true
|
||||
"id": "audio"
|
||||
});
|
||||
// Show Bluetooth only if available
|
||||
if (BluetoothService.available)
|
||||
tabs.push({
|
||||
"name": "Bluetooth",
|
||||
"text": "Bluetooth",
|
||||
"icon": "bluetooth",
|
||||
"id": "bluetooth",
|
||||
"available": true
|
||||
"id": "bluetooth"
|
||||
});
|
||||
|
||||
// Always show display
|
||||
tabs.push({
|
||||
"name": "Display",
|
||||
"text": "Display",
|
||||
"icon": "brightness_6",
|
||||
"id": "display",
|
||||
"available": true
|
||||
"id": "display"
|
||||
});
|
||||
return tabs;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property int tabCount: {
|
||||
let count = 3; // Network + Audio + Display (always visible)
|
||||
if (BluetoothService.available)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
width: (parent.width - Theme.spacingXS * (tabCount - 1)) / tabCount
|
||||
height: 40
|
||||
radius: Theme.cornerRadius
|
||||
color: root.currentTab === modelData.id ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : tabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: modelData.icon
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: root.currentTab === modelData.id ? Theme.primary : Theme.surfaceText
|
||||
font.weight: root.currentTab === modelData.id ? Font.Medium : Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: tabArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
root.currentTab = modelData.id;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onTabClicked: function(index) {
|
||||
let tabs = ["network", "audio"];
|
||||
if (BluetoothService.available)
|
||||
tabs.push("bluetooth");
|
||||
tabs.push("display");
|
||||
root.currentTab = tabs[index];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Tab content area
|
||||
@@ -5,6 +5,7 @@ import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules
|
||||
import qs.Widgets
|
||||
|
||||
ScrollView {
|
||||
@@ -39,7 +40,7 @@ ScrollView {
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
CustomSlider {
|
||||
DankSlider {
|
||||
width: parent.width
|
||||
value: BrightnessService.brightnessLevel
|
||||
leftIcon: "brightness_low"
|
||||
@@ -97,10 +98,9 @@ ScrollView {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: Prefs.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
DankIcon {
|
||||
name: Prefs.nightModeEnabled ? "nightlight" : "dark_mode"
|
||||
size: Theme.iconSizeLarge
|
||||
color: Prefs.nightModeEnabled ? Theme.primary : Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -149,10 +149,9 @@ ScrollView {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: Theme.isLightMode ? "light_mode" : "palette"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge
|
||||
DankIcon {
|
||||
name: Theme.isLightMode ? "light_mode" : "palette"
|
||||
size: Theme.iconSizeLarge
|
||||
color: Theme.isLightMode ? Theme.primary : Theme.surfaceText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import Quickshell.Io
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import "../../Widgets"
|
||||
|
||||
Item {
|
||||
// Default to WiFi when nothing is connected
|
||||
@@ -26,97 +28,29 @@ Item {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Network sub-tabs
|
||||
Row {
|
||||
DankTabBar {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingXS) / 2
|
||||
height: 36
|
||||
radius: Theme.cornerRadiusSmall
|
||||
color: networkTab.networkSubTab === 0 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : ethernetTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "lan"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
color: networkTab.networkSubTab === 0 ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Ethernet"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: networkTab.networkSubTab === 0 ? Theme.primary : Theme.surfaceText
|
||||
font.weight: networkTab.networkSubTab === 0 ? Font.Medium : Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
currentIndex: networkTab.networkSubTab
|
||||
model: [
|
||||
{
|
||||
"icon": "lan",
|
||||
"text": "Ethernet"
|
||||
},
|
||||
{
|
||||
"icon": NetworkService.wifiEnabled ? "wifi" : "wifi_off",
|
||||
"text": "Wi-Fi"
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: ethernetTabArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
networkTab.networkSubTab = 0;
|
||||
WifiService.autoRefreshEnabled = false;
|
||||
}
|
||||
]
|
||||
onTabClicked: function(index) {
|
||||
networkTab.networkSubTab = index;
|
||||
if (index === 0) {
|
||||
WifiService.autoRefreshEnabled = false;
|
||||
} else {
|
||||
WifiService.autoRefreshEnabled = true;
|
||||
if (NetworkService.wifiEnabled)
|
||||
WifiService.scanWifi();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: (parent.width - Theme.spacingXS) / 2
|
||||
height: 36
|
||||
radius: Theme.cornerRadiusSmall
|
||||
color: networkTab.networkSubTab === 1 ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : wifiTabArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: NetworkService.wifiEnabled ? "wifi" : "wifi_off"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
color: networkTab.networkSubTab === 1 ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Wi-Fi"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: networkTab.networkSubTab === 1 ? Theme.primary : Theme.surfaceText
|
||||
font.weight: networkTab.networkSubTab === 1 ? Font.Medium : Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiTabArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
networkTab.networkSubTab = 1;
|
||||
WifiService.autoRefreshEnabled = true;
|
||||
if (NetworkService.wifiEnabled)
|
||||
WifiService.scanWifi();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ethernet Tab Content
|
||||
@@ -153,10 +87,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "lan"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge - 4
|
||||
DankIcon {
|
||||
name: "lan"
|
||||
size: Theme.iconSizeLarge - 4
|
||||
color: networkTab.networkStatus === "ethernet" ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -197,12 +130,11 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
id: ethernetPreferenceIcon
|
||||
|
||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
name: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
size: Theme.fontSizeSmall
|
||||
color: networkTab.networkStatus === "ethernet" ? Theme.background : Theme.primary
|
||||
visible: networkTab.changingNetworkPreference
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -275,10 +207,9 @@ Item {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: networkTab.ethernetConnected ? "link_off" : "link"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: networkTab.ethernetConnected ? "link_off" : "link"
|
||||
size: Theme.iconSize
|
||||
color: networkTab.ethernetConnected ? Theme.error : Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -344,11 +275,11 @@ Item {
|
||||
visible: NetworkService.wifiAvailable
|
||||
|
||||
// WiFi icon
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: {
|
||||
name: {
|
||||
if (!NetworkService.wifiEnabled) {
|
||||
return "wifi_off";
|
||||
} else if (NetworkService.networkStatus === "wifi") {
|
||||
@@ -357,8 +288,7 @@ Item {
|
||||
return "wifi";
|
||||
}
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
size: Theme.iconSize
|
||||
color: NetworkService.networkStatus === "wifi" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -402,70 +332,16 @@ Item {
|
||||
}
|
||||
|
||||
// WiFi toggle switch
|
||||
Rectangle {
|
||||
width: 48
|
||||
height: 24
|
||||
radius: 12
|
||||
color: NetworkService.wifiEnabled ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
DankToggle {
|
||||
checked: NetworkService.wifiEnabled
|
||||
enabled: true
|
||||
toggling: NetworkService.wifiToggling
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
opacity: NetworkService.wifiToggling ? 0.6 : 1
|
||||
|
||||
Rectangle {
|
||||
id: toggleHandle
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: Theme.surface
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: NetworkService.wifiEnabled ? parent.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
// Subtle shadow/glow effect
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 2
|
||||
height: parent.height + 2
|
||||
radius: (parent.width + 2) / 2
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(0, 0, 0, 0.1)
|
||||
border.width: 1
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: wifiToggleArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
NetworkService.toggleWifiRadio();
|
||||
// Refresh network status and WiFi info after toggle with delay
|
||||
refreshTimer.triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
onClicked: {
|
||||
NetworkService.toggleWifiRadio();
|
||||
refreshTimer.triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -487,12 +363,11 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
id: wifiPreferenceIcon
|
||||
|
||||
text: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
name: networkTab.changingNetworkPreference ? "sync" : ""
|
||||
size: Theme.fontSizeSmall
|
||||
color: networkTab.networkStatus === "wifi" ? Theme.background : Theme.primary
|
||||
visible: networkTab.changingNetworkPreference
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -574,13 +449,12 @@ Item {
|
||||
radius: 16
|
||||
color: refreshArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : WifiService.isScanning ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
id: refreshIcon
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: WifiService.isScanning ? "sync" : "refresh"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
name: WifiService.isScanning ? "sync" : "refresh"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceText
|
||||
rotation: WifiService.isScanning ? refreshIcon.rotation : 0
|
||||
|
||||
@@ -652,10 +526,10 @@ Item {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
id: connectionIcon
|
||||
|
||||
text: {
|
||||
name: {
|
||||
if (WifiService.connectionStatus === "connecting")
|
||||
return "sync";
|
||||
|
||||
@@ -667,8 +541,7 @@ Item {
|
||||
|
||||
return "";
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
size: Theme.iconSize - 6
|
||||
color: {
|
||||
if (WifiService.connectionStatus === "connecting")
|
||||
return Theme.warning;
|
||||
@@ -762,14 +635,13 @@ Item {
|
||||
anchors.margins: Theme.spacingS
|
||||
|
||||
// Signal strength icon
|
||||
Text {
|
||||
DankIcon {
|
||||
id: signalIcon
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: modelData.signalStrength === "excellent" ? "wifi" : modelData.signalStrength === "good" ? "wifi_2_bar" : modelData.signalStrength === "fair" ? "wifi_1_bar" : modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
name: modelData.signalStrength === "excellent" ? "wifi" : modelData.signalStrength === "good" ? "wifi_2_bar" : modelData.signalStrength === "fair" ? "wifi_1_bar" : modelData.signalStrength === "poor" ? "wifi_calling_3" : "wifi"
|
||||
size: Theme.iconSize
|
||||
color: modelData.connected ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -818,10 +690,9 @@ Item {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
// Lock icon (if secured)
|
||||
Text {
|
||||
text: "lock"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
DankIcon {
|
||||
name: "lock"
|
||||
size: Theme.iconSize - 6
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
visible: modelData.secured
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -835,11 +706,10 @@ Item {
|
||||
color: forgetArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||
visible: modelData.saved || modelData.connected
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "delete"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
name: "delete"
|
||||
size: Theme.iconSize - 6
|
||||
color: forgetArea.containsMouse ? Theme.error : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -900,11 +770,10 @@ Item {
|
||||
visible: !NetworkService.wifiEnabled
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "wifi_off"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 48
|
||||
name: "wifi_off"
|
||||
size: 48
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: cpuWidget
|
||||
@@ -32,11 +33,9 @@ Rectangle {
|
||||
spacing: 3
|
||||
|
||||
// CPU icon
|
||||
Text {
|
||||
text: "memory" // Material Design memory icon (swapped from RAM widget)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
DankIcon {
|
||||
name: "memory" // Material Design memory icon (swapped from RAM widget)
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (SystemMonitorService.cpuUsage > 80)
|
||||
return Theme.error;
|
||||
@@ -6,6 +6,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: root
|
||||
@@ -160,10 +161,9 @@ PanelWindow {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: "delete_sweep"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeSmall
|
||||
DankIcon {
|
||||
name: "delete_sweep"
|
||||
size: Theme.iconSizeSmall
|
||||
color: clearArea.containsMouse ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -496,11 +496,10 @@ PanelWindow {
|
||||
color: expandArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
visible: modelData.count > 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "expand_more"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "expand_more"
|
||||
size: 18
|
||||
color: Theme.surfaceText
|
||||
rotation: expanded ? 180 : 0
|
||||
|
||||
@@ -532,11 +531,10 @@ PanelWindow {
|
||||
anchors.right: parent.right
|
||||
color: dismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "close"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -599,11 +597,10 @@ PanelWindow {
|
||||
border.color: quickReplyField.text.length > 0 ? "transparent" : Theme.outline
|
||||
border.width: quickReplyField.text.length > 0 ? 0 : 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "send"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "send"
|
||||
size: 16
|
||||
color: quickReplyField.text.length > 0 ? Theme.primaryText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -702,11 +699,10 @@ PanelWindow {
|
||||
anchors.left: parent.left
|
||||
color: collapseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "expand_less"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "expand_less"
|
||||
size: 18
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -728,11 +724,10 @@ PanelWindow {
|
||||
anchors.right: parent.right
|
||||
color: dismissAllArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "close"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -810,11 +805,10 @@ PanelWindow {
|
||||
anchors.top: parent.top
|
||||
color: individualDismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "close"
|
||||
size: 12
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -901,11 +895,10 @@ PanelWindow {
|
||||
radius: 14
|
||||
color: replyField.text.length > 0 ? Theme.primary : Theme.surfaceContainer
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "send"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "send"
|
||||
size: 12
|
||||
color: replyField.text.length > 0 ? Theme.primaryText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -974,11 +967,10 @@ PanelWindow {
|
||||
spacing: Theme.spacingM
|
||||
width: parent.width * 0.8
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "notifications_none"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSizeLarge + 16
|
||||
name: "notifications_none"
|
||||
size: Theme.iconSizeLarge + 16
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: notificationPopup
|
||||
@@ -268,11 +269,10 @@ PanelWindow {
|
||||
color: expandArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
visible: modelData.count > 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "expand_more"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "expand_more"
|
||||
size: 18
|
||||
color: Theme.surfaceText
|
||||
rotation: expanded ? 180 : 0
|
||||
|
||||
@@ -309,11 +309,10 @@ PanelWindow {
|
||||
anchors.right: parent.right
|
||||
color: dismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "close"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -376,11 +375,10 @@ PanelWindow {
|
||||
border.color: quickReplyField.text.length > 0 ? "transparent" : Theme.outline
|
||||
border.width: quickReplyField.text.length > 0 ? 0 : 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "send"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "send"
|
||||
size: 16
|
||||
color: quickReplyField.text.length > 0 ? Theme.primaryText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -487,11 +485,10 @@ PanelWindow {
|
||||
anchors.left: parent.left
|
||||
color: collapseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "expand_less"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "expand_less"
|
||||
size: 18
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -517,11 +514,10 @@ PanelWindow {
|
||||
anchors.right: parent.right
|
||||
color: dismissAllArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 16
|
||||
name: "close"
|
||||
size: 16
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -612,11 +608,10 @@ PanelWindow {
|
||||
anchors.top: parent.top
|
||||
color: individualDismissArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "close"
|
||||
size: 12
|
||||
color: Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -706,11 +701,10 @@ PanelWindow {
|
||||
radius: 14
|
||||
color: replyField.text.length > 0 ? Theme.primary : Theme.surfaceContainer
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "send"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "send"
|
||||
size: 12
|
||||
color: replyField.text.length > 0 ? Theme.primaryText : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: processListWidget
|
||||
@@ -213,8 +214,8 @@ PanelWindow {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
// Tab icons for visual hierarchy
|
||||
Text {
|
||||
text: {
|
||||
DankIcon {
|
||||
name: {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return "list_alt";
|
||||
@@ -226,8 +227,7 @@ PanelWindow {
|
||||
return "tab";
|
||||
}
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
size: Theme.iconSize - 2
|
||||
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
||||
opacity: currentTab === index ? 1 : 0.7
|
||||
|
||||
@@ -706,10 +706,9 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: ProcessMonitorService.getProcessIcon(modelData ? modelData.command : "")
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: ProcessMonitorService.getProcessIcon(modelData ? modelData.command : "")
|
||||
size: Theme.iconSize - 4
|
||||
color: {
|
||||
if (modelData && modelData.cpu > 80)
|
||||
return Theme.error;
|
||||
@@ -822,11 +821,9 @@ PanelWindow {
|
||||
color: menuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
text: "more_vert"
|
||||
font.family: Theme.iconFont
|
||||
font.weight: Theme.iconFontWeight
|
||||
font.pixelSize: Theme.iconSize - 2
|
||||
DankIcon {
|
||||
name: "more_vert"
|
||||
size: Theme.iconSize - 2
|
||||
color: Theme.surfaceText
|
||||
opacity: 0.6
|
||||
anchors.centerIn: parent
|
||||
@@ -1310,10 +1307,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "computer"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "computer"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -1382,10 +1378,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "developer_board"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "developer_board"
|
||||
size: Theme.iconSize
|
||||
color: Theme.secondary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -1461,10 +1456,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "timer"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "timer"
|
||||
size: Theme.iconSize
|
||||
color: Theme.warning
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -1526,10 +1520,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "list_alt"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "list_alt"
|
||||
size: Theme.iconSize
|
||||
color: Theme.info
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -1583,10 +1576,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Text {
|
||||
text: "storage"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "storage"
|
||||
size: Theme.iconSize
|
||||
color: Theme.success
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: ramWidget
|
||||
@@ -32,11 +33,9 @@ Rectangle {
|
||||
spacing: 3
|
||||
|
||||
// RAM icon
|
||||
Text {
|
||||
text: "developer_board" // Material Design CPU/processor icon (swapped from CPU widget)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
DankIcon {
|
||||
name: "developer_board" // Material Design CPU/processor icon (swapped from CPU widget)
|
||||
size: Theme.iconSize - 8
|
||||
color: {
|
||||
if (SystemMonitorService.memoryUsage > 90)
|
||||
return Theme.error;
|
||||
@@ -6,6 +6,7 @@ import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: settingsPopup
|
||||
@@ -74,10 +75,9 @@ PanelWindow {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
text: "settings"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: "settings"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
@@ -102,10 +102,9 @@ PanelWindow {
|
||||
radius: Theme.cornerRadius
|
||||
color: closeButton.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
||||
|
||||
Text {
|
||||
text: "close"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: "close"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
@@ -234,22 +233,20 @@ PanelWindow {
|
||||
color: Theme.primary
|
||||
visible: !parent.hasImage
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "person"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
name: "person"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Error icon for when the image fails to load.
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "warning"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize + 8
|
||||
name: "warning"
|
||||
size: Theme.iconSize + 8
|
||||
color: Theme.primaryText
|
||||
visible: profileImageInput.text !== "" && avatarImageSource.status === Image.Error
|
||||
}
|
||||
@@ -540,7 +537,7 @@ PanelWindow {
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
CustomSlider {
|
||||
DankSlider {
|
||||
width: parent.width
|
||||
value: Math.round(Prefs.topBarTransparency * 100)
|
||||
minimum: 0
|
||||
@@ -577,7 +574,7 @@ PanelWindow {
|
||||
font.weight: Font.Medium
|
||||
}
|
||||
|
||||
CustomSlider {
|
||||
DankSlider {
|
||||
width: parent.width
|
||||
value: Math.round(Prefs.popupTransparency * 100)
|
||||
minimum: 0
|
||||
@@ -7,6 +7,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
id: spotlightLauncher
|
||||
@@ -155,8 +156,8 @@ PanelWindow {
|
||||
function selectNext() {
|
||||
if (filteredApps.length > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns (6 columns)
|
||||
var columnsCount = 6;
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = resultsGrid.columns || 6;
|
||||
selectedIndex = Math.min(selectedIndex + columnsCount, filteredApps.length - 1);
|
||||
} else {
|
||||
// List navigation: next item
|
||||
@@ -168,8 +169,8 @@ PanelWindow {
|
||||
function selectPrevious() {
|
||||
if (filteredApps.length > 0) {
|
||||
if (viewMode === "grid") {
|
||||
// Grid navigation: move by columns (6 columns)
|
||||
var columnsCount = 6;
|
||||
// Grid navigation: move by columns
|
||||
var columnsCount = resultsGrid.columns || 6;
|
||||
selectedIndex = Math.max(selectedIndex - columnsCount, 0);
|
||||
} else {
|
||||
// List navigation: previous item
|
||||
@@ -465,11 +466,10 @@ PanelWindow {
|
||||
anchors.rightMargin: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "search"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
name: "search"
|
||||
size: Theme.iconSize
|
||||
color: searchField.activeFocus ? Theme.primary : Theme.surfaceVariantText
|
||||
}
|
||||
|
||||
@@ -553,11 +553,10 @@ PanelWindow {
|
||||
border.color: viewMode === "list" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : "transparent"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "view_list"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "view_list"
|
||||
size: 18
|
||||
color: viewMode === "list" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -584,11 +583,10 @@ PanelWindow {
|
||||
border.color: viewMode === "grid" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) : "transparent"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "grid_view"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 18
|
||||
name: "grid_view"
|
||||
size: 18
|
||||
color: viewMode === "grid" ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -619,256 +617,42 @@ PanelWindow {
|
||||
color: "transparent"
|
||||
|
||||
// List view
|
||||
ListView {
|
||||
DankListView {
|
||||
id: resultsList
|
||||
|
||||
// Make mouse wheel scrolling more responsive
|
||||
property real wheelStepSize: 60
|
||||
|
||||
anchors.fill: parent
|
||||
visible: viewMode === "list"
|
||||
model: filteredModel
|
||||
currentIndex: selectedIndex
|
||||
clip: true
|
||||
focus: spotlightOpen
|
||||
interactive: true
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120; // Standard wheel step
|
||||
resultsList.contentY -= steps * resultsList.wheelStepSize;
|
||||
// Ensure we stay within bounds
|
||||
if (resultsList.contentY < 0)
|
||||
resultsList.contentY = 0;
|
||||
else if (resultsList.contentY > resultsList.contentHeight - resultsList.height)
|
||||
resultsList.contentY = Math.max(0, resultsList.contentHeight - resultsList.height);
|
||||
}
|
||||
itemHeight: 60
|
||||
iconSize: 40
|
||||
showDescription: true
|
||||
onItemClicked: function(index, modelData) {
|
||||
launchApp(modelData);
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
width: resultsList.width
|
||||
height: 60
|
||||
radius: Theme.cornerRadius
|
||||
color: ListView.isCurrentItem ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : listMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Item {
|
||||
width: 40
|
||||
height: 40
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
IconImage {
|
||||
id: listIconImg
|
||||
|
||||
anchors.fill: parent
|
||||
source: model.icon ? Quickshell.iconPath(model.icon, "") : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: !listIconImg.visible
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
font.pixelSize: 20
|
||||
color: Theme.primary
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: model.name
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: model.comment
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
visible: model.comment && model.comment.length > 0
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: listMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: selectedIndex = index
|
||||
onClicked: launchApp(model)
|
||||
}
|
||||
|
||||
onItemHovered: function(index) {
|
||||
selectedIndex = index;
|
||||
}
|
||||
|
||||
ScrollBar.vertical: ScrollBar {
|
||||
policy: ScrollBar.AlwaysOn
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Grid view scroll container
|
||||
ScrollView {
|
||||
// Grid view
|
||||
DankGridView {
|
||||
id: resultsGrid
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
visible: viewMode === "grid"
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
GridView {
|
||||
id: resultsGrid
|
||||
|
||||
// Responsive cell sizes based on screen width - wider cells for better fill
|
||||
property int baseCellWidth: Math.max(120, Math.min(160, width / 6))
|
||||
property int baseCellHeight: baseCellWidth + 20
|
||||
// Center the grid content with better fill
|
||||
property int columnsCount: Math.floor(width / cellWidth)
|
||||
property int remainingSpace: width - (columnsCount * cellWidth)
|
||||
// Make mouse wheel scrolling more responsive
|
||||
property real wheelStepSize: 60
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
model: filteredModel
|
||||
focus: spotlightOpen
|
||||
interactive: true
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
cellWidth: baseCellWidth
|
||||
cellHeight: baseCellHeight
|
||||
leftMargin: Math.max(Theme.spacingXS, remainingSpace / 2)
|
||||
rightMargin: leftMargin
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: (wheel) => {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120; // Standard wheel step
|
||||
resultsGrid.contentY -= steps * resultsGrid.wheelStepSize;
|
||||
// Ensure we stay within bounds
|
||||
if (resultsGrid.contentY < 0)
|
||||
resultsGrid.contentY = 0;
|
||||
else if (resultsGrid.contentY > resultsGrid.contentHeight - resultsGrid.height)
|
||||
resultsGrid.contentY = Math.max(0, resultsGrid.contentHeight - resultsGrid.height);
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
width: resultsGrid.cellWidth - 8
|
||||
height: resultsGrid.cellHeight - 8
|
||||
radius: Theme.cornerRadius
|
||||
color: selectedIndex === index ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : gridMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
property int iconSize: Math.min(48, Math.max(32, resultsGrid.cellWidth * 0.55))
|
||||
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
IconImage {
|
||||
id: iconImg
|
||||
|
||||
anchors.fill: parent
|
||||
source: model.icon ? Quickshell.iconPath(model.icon, "") : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: !iconImg.visible
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
font.pixelSize: Math.min(24, parent.width * 0.5)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: resultsGrid.cellWidth - 12
|
||||
text: model.name
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: gridMouseArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: selectedIndex = index
|
||||
onClicked: launchApp(model)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
model: filteredModel
|
||||
columns: 6
|
||||
adaptiveColumns: true
|
||||
minCellWidth: 120
|
||||
maxCellWidth: 160
|
||||
iconSizeRatio: 0.55
|
||||
maxIconSize: 48
|
||||
currentIndex: selectedIndex
|
||||
onItemClicked: function(index, modelData) {
|
||||
launchApp(modelData);
|
||||
}
|
||||
onItemHovered: function(index) {
|
||||
selectedIndex = index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -21,8 +22,8 @@ Rectangle {
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
// Network Status Icon
|
||||
Text {
|
||||
text: {
|
||||
DankIcon {
|
||||
name: {
|
||||
if (NetworkService.networkStatus === "ethernet") {
|
||||
return "lan";
|
||||
} else if (NetworkService.networkStatus === "wifi") {
|
||||
@@ -42,20 +43,16 @@ Rectangle {
|
||||
return "wifi_off";
|
||||
}
|
||||
}
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
size: Theme.iconSize - 8
|
||||
color: NetworkService.networkStatus !== "disconnected" ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: true
|
||||
}
|
||||
|
||||
// Bluetooth Icon (when available and enabled) - moved next to network
|
||||
Text {
|
||||
text: "bluetooth"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
DankIcon {
|
||||
name: "bluetooth"
|
||||
size: Theme.iconSize - 8
|
||||
color: BluetoothService.enabled ? Theme.primary : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: BluetoothService.available && BluetoothService.enabled
|
||||
@@ -68,13 +65,11 @@ Rectangle {
|
||||
color: "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
id: audioIcon
|
||||
|
||||
text: AudioService.sinkMuted ? "volume_off" : AudioService.volumeLevel < 33 ? "volume_down" : "volume_up"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
name: AudioService.sinkMuted ? "volume_off" : AudioService.volumeLevel < 33 ? "volume_down" : "volume_up"
|
||||
size: Theme.iconSize - 8
|
||||
color: audioWheelArea.containsMouse || controlCenterArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
@@ -107,11 +102,9 @@ Rectangle {
|
||||
}
|
||||
|
||||
// Microphone Icon (when active)
|
||||
Text {
|
||||
text: "mic"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 8
|
||||
font.weight: Theme.iconFontWeight
|
||||
DankIcon {
|
||||
name: "mic"
|
||||
size: Theme.iconSize - 8
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: false // TODO: Add mic detection
|
||||
@@ -2,6 +2,7 @@ import QtQuick
|
||||
import Quickshell.Services.Mpris
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -148,11 +149,10 @@ Rectangle {
|
||||
visible: root.playerAvailable
|
||||
opacity: (activePlayer && activePlayer.canGoPrevious) ? 1 : 0.3
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_previous"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "skip_previous"
|
||||
size: 12
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -181,11 +181,10 @@ Rectangle {
|
||||
visible: root.playerAvailable
|
||||
opacity: activePlayer ? 1 : 0.3
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 14
|
||||
name: activePlayer && activePlayer.playbackState === 1 ? "pause" : "play_arrow"
|
||||
size: 14
|
||||
color: activePlayer && activePlayer.playbackState === 1 ? Theme.background : Theme.primary
|
||||
}
|
||||
|
||||
@@ -212,11 +211,10 @@ Rectangle {
|
||||
visible: playerAvailable
|
||||
opacity: (activePlayer && activePlayer.canGoNext) ? 1 : 0.3
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "skip_next"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: 12
|
||||
name: "skip_next"
|
||||
size: 12
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -14,12 +15,10 @@ Rectangle {
|
||||
radius: Theme.cornerRadius
|
||||
color: notificationArea.containsMouse || root.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08)
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "notifications"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
font.weight: Theme.iconFontWeight
|
||||
name: "notifications"
|
||||
size: Theme.iconSize - 6
|
||||
color: notificationArea.containsMouse || root.isActive ? Theme.primary : Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import Quickshell.Wayland
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Modules
|
||||
import qs.Widgets
|
||||
|
||||
PanelWindow {
|
||||
@@ -204,12 +205,10 @@ PanelWindow {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: Prefs.showClipboard
|
||||
|
||||
Text {
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
text: "content_paste"
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 6
|
||||
font.weight: Theme.iconFontWeight
|
||||
name: "content_paste"
|
||||
size: Theme.iconSize - 6
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
@@ -19,10 +20,9 @@ Rectangle {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
text: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize - 4
|
||||
DankIcon {
|
||||
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
148
Widgets/DankGridView.qml
Normal file
148
Widgets/DankGridView.qml
Normal file
@@ -0,0 +1,148 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
|
||||
ScrollView {
|
||||
id: gridView
|
||||
|
||||
property alias model: grid.model
|
||||
property int currentIndex: 0
|
||||
property int columns: 4
|
||||
property bool adaptiveColumns: false
|
||||
property int minCellWidth: 120
|
||||
property int maxCellWidth: 160
|
||||
property int cellPadding: 8
|
||||
property real iconSizeRatio: 0.6
|
||||
property int maxIconSize: 56
|
||||
property int minIconSize: 32
|
||||
property real wheelStepSize: 60
|
||||
|
||||
signal itemClicked(int index, var modelData)
|
||||
signal itemHovered(int index)
|
||||
|
||||
clip: true
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
GridView {
|
||||
id: grid
|
||||
|
||||
property int baseCellWidth: adaptiveColumns ?
|
||||
Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) :
|
||||
(width - Theme.spacingS * 2) / columns
|
||||
property int baseCellHeight: baseCellWidth + 20
|
||||
property int actualColumns: adaptiveColumns ? Math.floor(width / cellWidth) : columns
|
||||
property int remainingSpace: width - (actualColumns * cellWidth)
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
cellWidth: baseCellWidth
|
||||
cellHeight: baseCellHeight
|
||||
leftMargin: Math.max(Theme.spacingS, remainingSpace / 2)
|
||||
rightMargin: leftMargin
|
||||
focus: true
|
||||
interactive: true
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: function(wheel) {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120;
|
||||
grid.contentY -= steps * wheelStepSize;
|
||||
if (grid.contentY < 0)
|
||||
grid.contentY = 0;
|
||||
else if (grid.contentY > grid.contentHeight - grid.height)
|
||||
grid.contentY = Math.max(0, grid.contentHeight - grid.height);
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
width: grid.cellWidth - cellPadding
|
||||
height: grid.cellHeight - cellPadding
|
||||
radius: Theme.cornerRadiusLarge
|
||||
color: currentIndex === index ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
||||
mouseArea.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: currentIndex === index ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) :
|
||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: currentIndex === index ? 2 : 1
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
property int iconSize: Math.min(maxIconSize, Math.max(minIconSize, grid.cellWidth * iconSizeRatio))
|
||||
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
IconImage {
|
||||
id: iconImg
|
||||
anchors.fill: parent
|
||||
source: (model.icon) ? Quickshell.iconPath(model.icon, "") : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: !iconImg.visible
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
font.pixelSize: Math.min(28, parent.width * 0.5)
|
||||
color: Theme.primary
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: grid.cellWidth - 12
|
||||
text: model.name || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
maximumLineCount: 2
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: {
|
||||
currentIndex = index;
|
||||
itemHovered(index);
|
||||
}
|
||||
onClicked: {
|
||||
itemClicked(index, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Widgets/DankIcon.qml
Normal file
26
Widgets/DankIcon.qml
Normal file
@@ -0,0 +1,26 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Text {
|
||||
id: icon
|
||||
|
||||
property alias name: icon.text
|
||||
property alias size: icon.font.pixelSize
|
||||
property alias color: icon.color
|
||||
property bool filled: false
|
||||
readonly property string iconFont : {
|
||||
var families = Qt.fontFamilies();
|
||||
if (families.indexOf("Material Symbols Rounded") !== -1) {
|
||||
return "Material Symbols Rounded";
|
||||
} else {
|
||||
return "Material Icons Round";
|
||||
}
|
||||
}
|
||||
|
||||
font.family: iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
font.weight: filled ? Font.Medium : Font.Normal
|
||||
color: Theme.surfaceText
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
145
Widgets/DankListView.qml
Normal file
145
Widgets/DankListView.qml
Normal file
@@ -0,0 +1,145 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import qs.Common
|
||||
|
||||
ScrollView {
|
||||
id: listView
|
||||
|
||||
property alias model: list.model
|
||||
property int currentIndex: 0
|
||||
property int itemHeight: 72
|
||||
property int iconSize: 56
|
||||
property real wheelStepSize: 60
|
||||
property bool showDescription: true
|
||||
property int itemSpacing: Theme.spacingS
|
||||
|
||||
signal itemClicked(int index, var modelData)
|
||||
signal itemHovered(int index)
|
||||
|
||||
clip: true
|
||||
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: list
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: itemSpacing
|
||||
spacing: listView.itemSpacing
|
||||
focus: true
|
||||
interactive: true
|
||||
currentIndex: listView.currentIndex
|
||||
flickDeceleration: 8000
|
||||
maximumFlickVelocity: 15000
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
propagateComposedEvents: true
|
||||
z: -1
|
||||
onWheel: function(wheel) {
|
||||
var delta = wheel.angleDelta.y;
|
||||
var steps = delta / 120;
|
||||
list.contentY -= steps * wheelStepSize;
|
||||
if (list.contentY < 0)
|
||||
list.contentY = 0;
|
||||
else if (list.contentY > list.contentHeight - list.height)
|
||||
list.contentY = Math.max(0, list.contentHeight - list.height);
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Rectangle {
|
||||
width: list.width
|
||||
height: itemHeight
|
||||
radius: Theme.cornerRadiusLarge
|
||||
color: ListView.isCurrentItem ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
||||
mouseArea.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.03)
|
||||
border.color: ListView.isCurrentItem ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3) :
|
||||
Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||
border.width: ListView.isCurrentItem ? 2 : 1
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingL
|
||||
|
||||
Item {
|
||||
width: iconSize
|
||||
height: iconSize
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
IconImage {
|
||||
id: iconImg
|
||||
anchors.fill: parent
|
||||
source: (model.icon) ? Quickshell.iconPath(model.icon, "") : ""
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
visible: status === Image.Ready
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: !iconImg.visible
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
radius: Theme.cornerRadiusLarge
|
||||
border.width: 1
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: (model.name && model.name.length > 0) ? model.name.charAt(0).toUpperCase() : "A"
|
||||
font.pixelSize: iconSize * 0.4
|
||||
color: Theme.primary
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: parent.width - iconSize - Theme.spacingL
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: model.name || ""
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
Text {
|
||||
width: parent.width
|
||||
text: model.comment || "Application"
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
elide: Text.ElideRight
|
||||
visible: showDescription && model.comment && model.comment.length > 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
z: 10
|
||||
onEntered: {
|
||||
listView.currentIndex = index;
|
||||
itemHovered(index);
|
||||
}
|
||||
onClicked: {
|
||||
itemClicked(index, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: slider
|
||||
@@ -40,10 +41,9 @@ Item {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
// Left icon
|
||||
Text {
|
||||
text: slider.leftIcon
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: slider.leftIcon
|
||||
size: Theme.iconSize
|
||||
color: slider.enabled ? Theme.surfaceText : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: slider.leftIcon.length > 0
|
||||
@@ -205,10 +205,9 @@ Item {
|
||||
}
|
||||
|
||||
// Right icon
|
||||
Text {
|
||||
text: slider.rightIcon
|
||||
font.family: Theme.iconFont
|
||||
font.pixelSize: Theme.iconSize
|
||||
DankIcon {
|
||||
name: slider.rightIcon
|
||||
size: Theme.iconSize
|
||||
color: slider.enabled ? Theme.surfaceText : Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: slider.rightIcon.length > 0
|
||||
87
Widgets/DankTabBar.qml
Normal file
87
Widgets/DankTabBar.qml
Normal file
@@ -0,0 +1,87 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
|
||||
Item {
|
||||
id: tabBar
|
||||
|
||||
property alias model: tabRepeater.model
|
||||
property int currentIndex: 0
|
||||
property int spacing: Theme.spacingXS
|
||||
property int tabHeight: 36
|
||||
property bool showIcons: true
|
||||
property bool equalWidthTabs: true
|
||||
|
||||
signal tabClicked(int index)
|
||||
|
||||
height: tabHeight
|
||||
|
||||
Row {
|
||||
id: tabRow
|
||||
anchors.fill: parent
|
||||
spacing: tabBar.spacing
|
||||
|
||||
Repeater {
|
||||
id: tabRepeater
|
||||
|
||||
Rectangle {
|
||||
property int tabCount: tabRepeater.count
|
||||
property bool isActive: tabBar.currentIndex === index
|
||||
property bool hasIcon: tabBar.showIcons && modelData.icon && modelData.icon.length > 0
|
||||
property bool hasText: modelData.text && modelData.text.length > 0
|
||||
|
||||
width: tabBar.equalWidthTabs ?
|
||||
(tabBar.width - tabBar.spacing * (tabCount - 1)) / tabCount :
|
||||
contentRow.implicitWidth + Theme.spacingM * 2
|
||||
height: tabBar.tabHeight
|
||||
radius: Theme.cornerRadiusSmall
|
||||
color: isActive ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16) :
|
||||
tabArea.containsMouse ?
|
||||
Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) :
|
||||
"transparent"
|
||||
|
||||
Row {
|
||||
id: contentRow
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
DankIcon {
|
||||
name: modelData.icon || ""
|
||||
size: Theme.iconSize - 4
|
||||
color: parent.parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: parent.parent.hasIcon
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.text || ""
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: parent.parent.isActive ? Theme.primary : Theme.surfaceText
|
||||
font.weight: parent.parent.isActive ? Font.Medium : Font.Normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: parent.parent.hasText
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: tabArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
tabBar.currentIndex = index
|
||||
tabBar.tabClicked(index)
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
Widgets/DankToggle.qml
Normal file
75
Widgets/DankToggle.qml
Normal file
@@ -0,0 +1,75 @@
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
|
||||
Item {
|
||||
id: toggle
|
||||
|
||||
property bool checked: false
|
||||
property bool enabled: true
|
||||
property bool toggling: false
|
||||
|
||||
signal clicked()
|
||||
|
||||
width: 48
|
||||
height: 24
|
||||
|
||||
Rectangle {
|
||||
id: toggleTrack
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: height / 2
|
||||
color: toggle.checked ? Theme.primary : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
opacity: toggle.toggling ? 0.6 : 1
|
||||
|
||||
Rectangle {
|
||||
id: toggleHandle
|
||||
width: 20
|
||||
height: 20
|
||||
radius: 10
|
||||
color: Theme.surface
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: toggle.checked ? parent.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width + 2
|
||||
height: parent.height + 2
|
||||
radius: (parent.width + 2) / 2
|
||||
color: "transparent"
|
||||
border.color: Qt.rgba(0, 0, 0, 0.1)
|
||||
border.width: 1
|
||||
z: -1
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: toggleArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: toggle.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: toggle.enabled
|
||||
onClicked: toggle.clicked()
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
10
shell.qml
10
shell.qml
@@ -1,10 +1,10 @@
|
||||
//@ pragma UseQApplication
|
||||
|
||||
import Quickshell
|
||||
import qs.Widgets
|
||||
import qs.Widgets.CenterCommandCenter
|
||||
import qs.Widgets.ControlCenter
|
||||
import qs.Widgets.TopBar
|
||||
import qs.Modules
|
||||
import qs.Modules.CenterCommandCenter
|
||||
import qs.Modules.ControlCenter
|
||||
import qs.Modules.TopBar
|
||||
|
||||
ShellRoot {
|
||||
id: root
|
||||
@@ -85,7 +85,7 @@ ShellRoot {
|
||||
id: clipboardHistoryPopup
|
||||
}
|
||||
|
||||
ToastWidget {
|
||||
Toast {
|
||||
id: toastWidget
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user