1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-03 20:32:07 -04:00

refactor: mega refactoring of a bunch of things

This commit is contained in:
bbedward
2025-07-23 11:56:18 -04:00
parent 14eef59c9f
commit 19adcf3578
52 changed files with 4260 additions and 3879 deletions

View File

@@ -29,6 +29,7 @@ Rectangle {
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
@@ -40,5 +41,7 @@ Rectangle {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}

View File

@@ -19,6 +19,12 @@ Rectangle {
height: 60
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08)
// Global keyboard handler for escape key
Keys.onEscapePressed: {
if (dropdownMenu.visible)
dropdownMenu.visible = false;
}
Column {
anchors.left: parent.left
@@ -43,11 +49,12 @@ Rectangle {
wrapMode: Text.WordWrap
width: parent.width
}
}
Rectangle {
id: dropdown
width: 180
height: 36
anchors.right: parent.right
@@ -80,10 +87,12 @@ Rectangle {
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: dropdownArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
@@ -97,34 +106,35 @@ Rectangle {
}
}
}
}
// Integrated dropdown menu with full-screen overlay
PanelWindow {
id: dropdownMenu
property int targetX: 0
property int targetY: 0
function updatePosition() {
var globalPos = dropdown.mapToGlobal(0, 0);
targetX = globalPos.x;
targetY = globalPos.y + dropdown.height + 4;
}
visible: false
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
color: "transparent"
function updatePosition() {
var globalPos = dropdown.mapToGlobal(0, 0);
targetX = globalPos.x;
targetY = globalPos.y + dropdown.height + 4;
}
// Background click interceptor (invisible)
MouseArea {
anchors.fill: parent
@@ -133,7 +143,7 @@ Rectangle {
dropdownMenu.visible = false;
}
}
// Dropdown menu content
Rectangle {
x: dropdownMenu.targetX
@@ -141,25 +151,25 @@ Rectangle {
width: 180
height: Math.min(200, root.options.length * 36 + 16)
radius: Theme.cornerRadiusSmall
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 1.0)
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 1)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1
ScrollView {
anchors.fill: parent
anchors.margins: Theme.spacingS
clip: true
ListView {
model: root.options
spacing: 2
delegate: Rectangle {
width: ListView.view.width
height: 32
radius: Theme.cornerRadiusSmall
color: optionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
Text {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingS
@@ -169,9 +179,10 @@ Rectangle {
color: root.currentValue === modelData ? Theme.primary : Theme.surfaceText
font.weight: root.currentValue === modelData ? Font.Medium : Font.Normal
}
MouseArea {
id: optionArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
@@ -181,16 +192,15 @@ Rectangle {
dropdownMenu.visible = false;
}
}
}
}
}
}
}
// Global keyboard handler for escape key
Keys.onEscapePressed: {
if (dropdownMenu.visible) {
dropdownMenu.visible = false;
}
}
}
}

View File

@@ -30,9 +30,7 @@ ScrollView {
GridView {
id: grid
property int baseCellWidth: adaptiveColumns ?
Math.max(minCellWidth, Math.min(maxCellWidth, width / columns)) :
(width - Theme.spacingS * 2) / columns
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)
@@ -68,14 +66,8 @@ ScrollView {
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)
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 {
@@ -91,6 +83,7 @@ ScrollView {
IconImage {
id: iconImg
anchors.fill: parent
source: (model.icon) ? Quickshell.iconPath(model.icon, "") : ""
smooth: true
@@ -113,7 +106,9 @@ ScrollView {
color: Theme.primary
font.weight: Font.Bold
}
}
}
Text {
@@ -128,24 +123,29 @@ ScrollView {
maximumLineCount: 2
wrapMode: Text.WordWrap
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
z: 10
onEntered: {
if (hoverUpdatesSelection) {
if (hoverUpdatesSelection)
currentIndex = index;
}
itemHovered(index);
}
onClicked: {
itemClicked(index, model);
}
}
}
}
}
}

View File

@@ -3,7 +3,7 @@ import qs.Common
Text {
id: icon
property alias name: icon.text
property alias size: icon.font.pixelSize
property alias color: icon.color
@@ -15,4 +15,4 @@ Text {
color: Theme.surfaceText
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
}
}

View File

@@ -55,14 +55,8 @@ ScrollView {
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)
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 {
@@ -77,6 +71,7 @@ ScrollView {
IconImage {
id: iconImg
anchors.fill: parent
source: (model.icon) ? Quickshell.iconPath(model.icon, "") : ""
smooth: true
@@ -99,7 +94,9 @@ ScrollView {
color: Theme.primary
font.weight: Font.Bold
}
}
}
Column {
@@ -124,25 +121,31 @@ ScrollView {
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: {
if (hoverUpdatesSelection) {
if (hoverUpdatesSelection)
listView.currentIndex = index;
}
itemHovered(index);
}
onClicked: {
itemClicked(index, model);
}
}
}
}
}
}

View File

@@ -6,139 +6,139 @@ import qs.Widgets
Item {
id: root
property string currentLocation: ""
property string placeholderText: "Search for a location..."
signal locationSelected(string displayName, string coordinates)
width: parent.width
height: searchInputField.height + (searchDropdown.visible ? searchDropdown.height : 0)
property bool _internalChange: false
property bool isLoading: false
property string helperTextState: "default" // "default", "prompt", "searching", "found", "not_found"
property string currentSearchText: ""
signal locationSelected(string displayName, string coordinates)
function resetSearchState() {
locationSearchTimer.stop();
dropdownHideTimer.stop();
if (locationSearcher.running)
locationSearcher.running = false;
isLoading = false;
searchResultsModel.clear();
helperTextState = "default";
}
width: parent.width
height: searchInputField.height + (searchDropdown.visible ? searchDropdown.height : 0)
ListModel {
id: searchResultsModel
}
function resetSearchState() {
locationSearchTimer.stop()
dropdownHideTimer.stop()
if (locationSearcher.running) {
locationSearcher.running = false;
}
isLoading = false
searchResultsModel.clear()
helperTextState = "default"
}
Timer {
id: locationSearchTimer
interval: 500
running: false
repeat: false
onTriggered: {
if (locationInput.text.length > 2) {
if (locationSearcher.running) {
locationSearcher.running = false
}
searchResultsModel.clear()
root.isLoading = true
root.helperTextState = "searching"
const searchLocation = locationInput.text
root.currentSearchText = searchLocation
const encodedLocation = encodeURIComponent(searchLocation)
const curlCommand = `curl -s --connect-timeout 5 --max-time 10 'https://nominatim.openstreetmap.org/search?q=${encodedLocation}&format=json&limit=5&addressdetails=1'`
locationSearcher.command = ["bash", "-c", curlCommand]
locationSearcher.running = true
if (locationSearcher.running)
locationSearcher.running = false;
searchResultsModel.clear();
root.isLoading = true;
root.helperTextState = "searching";
const searchLocation = locationInput.text;
root.currentSearchText = searchLocation;
const encodedLocation = encodeURIComponent(searchLocation);
const curlCommand = `curl -s --connect-timeout 5 --max-time 10 'https://nominatim.openstreetmap.org/search?q=${encodedLocation}&format=json&limit=5&addressdetails=1'`;
locationSearcher.command = ["bash", "-c", curlCommand];
locationSearcher.running = true;
}
}
}
Timer {
id: dropdownHideTimer
interval: 200
running: false
repeat: false
onTriggered: {
if (!locationInput.getActiveFocus() && !searchDropdown.hovered) {
root.resetSearchState()
}
if (!locationInput.getActiveFocus() && !searchDropdown.hovered)
root.resetSearchState();
}
}
Process {
id: locationSearcher
command: ["bash", "-c", "echo"]
running: false
onExited: (exitCode) => {
root.isLoading = false;
if (exitCode !== 0) {
searchResultsModel.clear();
root.helperTextState = "not_found";
}
}
stdout: StdioCollector {
onStreamFinished: {
if (root.currentSearchText !== locationInput.text) {
return
}
const raw = text.trim()
root.isLoading = false
searchResultsModel.clear()
if (root.currentSearchText !== locationInput.text)
return ;
const raw = text.trim();
root.isLoading = false;
searchResultsModel.clear();
if (!raw || raw[0] !== "[") {
root.helperTextState = "not_found"
return
root.helperTextState = "not_found";
return ;
}
try {
const data = JSON.parse(raw)
const data = JSON.parse(raw);
if (data.length === 0) {
root.helperTextState = "not_found"
return
root.helperTextState = "not_found";
return ;
}
for (let i = 0; i < Math.min(data.length, 5); i++) {
const location = data[i]
const location = data[i];
if (location.display_name && location.lat && location.lon) {
const parts = location.display_name.split(', ')
let cleanName = parts[0]
const parts = location.display_name.split(', ');
let cleanName = parts[0];
if (parts.length > 1) {
const state = parts[parts.length - 2]
if (state && state !== cleanName) {
cleanName += `, ${state}`
}
const state = parts[parts.length - 2];
if (state && state !== cleanName)
cleanName += `, ${state}`;
}
const query = `${location.lat},${location.lon}`
searchResultsModel.append({ "name": cleanName, "query": query })
const query = `${location.lat},${location.lon}`;
searchResultsModel.append({
"name": cleanName,
"query": query
});
}
}
root.helperTextState = "found"
root.helperTextState = "found";
} catch (e) {
root.helperTextState = "not_found"
root.helperTextState = "not_found";
}
}
}
onExited: (exitCode) => {
root.isLoading = false
if (exitCode !== 0) {
searchResultsModel.clear()
root.helperTextState = "not_found"
}
}
}
// Search input field
Item {
id: searchInputField
width: parent.width
height: 48
DankTextField {
id: locationInput
width: parent.width
height: parent.height
leftIconName: "search"
@@ -147,123 +147,138 @@ Item {
backgroundColor: Theme.surfaceVariant
normalBorderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
focusedBorderColor: Theme.primary
onTextEdited: {
if (root._internalChange) return
if (root._internalChange)
return ;
if (getActiveFocus()) {
if (text.length > 2) {
root.isLoading = true
root.helperTextState = "searching"
locationSearchTimer.restart()
root.isLoading = true;
root.helperTextState = "searching";
locationSearchTimer.restart();
} else {
root.resetSearchState()
root.helperTextState = "prompt"
root.resetSearchState();
root.helperTextState = "prompt";
}
}
}
onFocusStateChanged: (hasFocus) => {
if (hasFocus) {
dropdownHideTimer.stop()
if (text.length <= 2) {
root.helperTextState = "prompt"
}
}
else {
dropdownHideTimer.start()
dropdownHideTimer.stop();
if (text.length <= 2)
root.helperTextState = "prompt";
} else {
dropdownHideTimer.start();
}
}
}
// Status icon overlay
DankIcon {
name: {
if (root.isLoading) return "hourglass_empty"
if (searchResultsModel.count > 0) return "check_circle"
if (locationInput.getActiveFocus() && locationInput.text.length > 2 && !root.isLoading) return "error"
return ""
if (root.isLoading)
return "hourglass_empty";
if (searchResultsModel.count > 0)
return "check_circle";
if (locationInput.getActiveFocus() && locationInput.text.length > 2 && !root.isLoading)
return "error";
return "";
}
size: Theme.iconSize - 4
color: {
if (root.isLoading) return Theme.surfaceVariantText
if (searchResultsModel.count > 0) return Theme.success || Theme.primary
if (locationInput.getActiveFocus() && locationInput.text.length > 2) return Theme.error
return "transparent"
if (root.isLoading)
return Theme.surfaceVariantText;
if (searchResultsModel.count > 0)
return Theme.success || Theme.primary;
if (locationInput.getActiveFocus() && locationInput.text.length > 2)
return Theme.error;
return "transparent";
}
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
opacity: (locationInput.getActiveFocus() && locationInput.text.length > 2) ? 1.0 : 0.0
opacity: (locationInput.getActiveFocus() && locationInput.text.length > 2) ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
// Search results dropdown
Rectangle {
id: searchDropdown
property bool hovered: false
width: parent.width
height: Math.min(Math.max(searchResultsModel.count * 38 + Theme.spacingS * 2, 50), 200)
y: searchInputField.height
radius: Theme.cornerRadius
color: Theme.popupBackground()
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.3)
border.width: 1
visible: locationInput.getActiveFocus() && locationInput.text.length > 2 && (searchResultsModel.count > 0 || root.isLoading)
property bool hovered: false
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.hovered = true
dropdownHideTimer.stop()
parent.hovered = true;
dropdownHideTimer.stop();
}
onExited: {
parent.hovered = false
if (!locationInput.getActiveFocus()) {
dropdownHideTimer.start()
}
parent.hovered = false;
if (!locationInput.getActiveFocus())
dropdownHideTimer.start();
}
acceptedButtons: Qt.NoButton
}
Item {
anchors.fill: parent
anchors.margins: Theme.spacingS
ListView {
id: searchResultsList
anchors.fill: parent
clip: true
model: searchResultsModel
spacing: 2
delegate: Rectangle {
width: searchResultsList.width
height: 36
radius: Theme.cornerRadius
color: resultMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1) : "transparent"
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingS
DankIcon {
name: "place"
size: Theme.iconSize - 6
color: Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: model.name || "Unknown"
font.pixelSize: Theme.fontSizeMedium
@@ -272,30 +287,31 @@ Item {
elide: Text.ElideRight
width: parent.width - 30
}
}
MouseArea {
id: resultMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root._internalChange = true
const selectedName = model.name
const selectedQuery = model.query
locationInput.text = selectedName
root.locationSelected(selectedName, selectedQuery)
root.resetSearchState()
locationInput.setFocus(false)
root._internalChange = false
root._internalChange = true;
const selectedName = model.name;
const selectedQuery = model.query;
locationInput.text = selectedName;
root.locationSelected(selectedName, selectedQuery);
root.resetSearchState();
locationInput.setFocus(false);
root._internalChange = false;
}
}
}
}
// Show message when no results
Text {
anchors.centerIn: parent
@@ -304,6 +320,9 @@ Item {
color: Theme.surfaceVariantText
visible: searchResultsList.count === 0 && locationInput.text.length > 2
}
}
}
}
}

View File

@@ -10,33 +10,29 @@ PanelWindow {
// Core properties
property alias content: contentLoader.sourceComponent
// Sizing - can use fixed or relative to screen
property real width: 400
property real height: 300
// Screen-relative sizing helpers
readonly property real screenWidth: screen ? screen.width : 1920
readonly property real screenHeight: screen ? screen.height : 1080
// Background behavior
property bool showBackground: true
property real backgroundOpacity: 0.5
// Positioning
property string positioning: "center" // "center", "top-right", "custom"
property string positioning: "center"
// "center", "top-right", "custom"
property point customPosition: Qt.point(0, 0)
// Focus management
property string keyboardFocus: "ondemand" // "ondemand", "exclusive", "none"
property string keyboardFocus: "ondemand"
// "ondemand", "exclusive", "none"
property bool closeOnEscapeKey: true
property bool closeOnBackgroundClick: true
// Animation
property string animationType: "scale" // "scale", "slide", "fade"
property string animationType: "scale"
// "scale", "slide", "fade"
property int animationDuration: Theme.mediumDuration
property var animationEasing: Theme.emphasizedEasing
// Styling
property color backgroundColor: Theme.surfaceContainer
property color borderColor: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
@@ -49,11 +45,47 @@ PanelWindow {
signal dialogClosed()
signal backgroundClicked()
// Convenience functions
function open() {
visible = true;
}
function close() {
visible = false;
}
function toggle() {
visible = !visible;
}
// PanelWindow configuration
// visible property is inherited from PanelWindow
color: "transparent"
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
switch (root.keyboardFocus) {
case "exclusive":
return WlrKeyboardFocus.Exclusive;
case "none":
return WlrKeyboardFocus.None;
default:
return WlrKeyboardFocus.OnDemand;
}
}
onVisibleChanged: {
if (root.visible) {
opened();
} else {
// Properly cleanup text input surfaces
if (Qt.inputMethod) {
Qt.inputMethod.hide();
Qt.inputMethod.reset();
}
dialogClosed();
}
}
anchors {
top: true
left: true
@@ -61,103 +93,77 @@ PanelWindow {
bottom: true
}
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: {
switch (root.keyboardFocus) {
case "exclusive": return WlrKeyboardFocus.Exclusive
case "none": return WlrKeyboardFocus.None
default: return WlrKeyboardFocus.OnDemand
}
}
onVisibleChanged: {
if (root.visible) {
opened()
} else {
// Properly cleanup text input surfaces
if (Qt.inputMethod) {
Qt.inputMethod.hide()
Qt.inputMethod.reset()
}
dialogClosed()
}
}
// Background overlay
Rectangle {
id: background
anchors.fill: parent
color: "black"
opacity: root.showBackground ? (root.visible ? root.backgroundOpacity : 0) : 0
visible: root.showBackground
MouseArea {
anchors.fill: parent
enabled: root.closeOnBackgroundClick
onClicked: (mouse) => {
var localPos = mapToItem(contentContainer, mouse.x, mouse.y);
if (localPos.x < 0 || localPos.x > contentContainer.width || localPos.y < 0 || localPos.y > contentContainer.height)
root.backgroundClicked();
}
}
Behavior on opacity {
NumberAnimation {
duration: root.animationDuration
easing.type: root.animationEasing
}
}
MouseArea {
anchors.fill: parent
enabled: root.closeOnBackgroundClick
onClicked: (mouse) => {
var localPos = mapToItem(contentContainer, mouse.x, mouse.y)
if (localPos.x < 0 || localPos.x > contentContainer.width ||
localPos.y < 0 || localPos.y > contentContainer.height) {
root.backgroundClicked()
}
}
}
}
// Main content container
Rectangle {
id: contentContainer
width: root.width
height: root.height
// Positioning
anchors.centerIn: positioning === "center" ? parent : undefined
x: {
if (positioning === "top-right") {
return Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL)
} else if (positioning === "custom") {
return root.customPosition.x
}
return 0 // Will be overridden by anchors.centerIn when positioning === "center"
if (positioning === "top-right")
return Math.max(Theme.spacingL, root.screenWidth - width - Theme.spacingL);
else if (positioning === "custom")
return root.customPosition.x;
return 0; // Will be overridden by anchors.centerIn when positioning === "center"
}
y: {
if (positioning === "top-right") {
return Theme.barHeight + Theme.spacingXS
} else if (positioning === "custom") {
return root.customPosition.y
}
return 0 // Will be overridden by anchors.centerIn when positioning === "center"
if (positioning === "top-right")
return Theme.barHeight + Theme.spacingXS;
else if (positioning === "custom")
return root.customPosition.y;
return 0; // Will be overridden by anchors.centerIn when positioning === "center"
}
color: root.backgroundColor
radius: root.cornerRadius
border.color: root.borderColor
border.width: root.borderWidth
layer.enabled: root.enableShadow
// Animation properties
opacity: root.visible ? 1 : 0
scale: {
if (root.animationType === "scale") {
return root.visible ? 1 : 0.9
}
return 1
}
if (root.animationType === "scale")
return root.visible ? 1 : 0.9;
return 1;
}
// Transform for slide animation
transform: root.animationType === "slide" ? slideTransform : null
Translate {
id: slideTransform
x: root.visible ? 0 : 15
y: root.visible ? 0 : -30
}
@@ -165,6 +171,7 @@ PanelWindow {
// Content area
Loader {
id: contentLoader
anchors.fill: parent
active: true
asynchronous: false
@@ -176,14 +183,17 @@ PanelWindow {
duration: root.animationDuration
easing.type: root.animationEasing
}
}
Behavior on scale {
enabled: root.animationType === "scale"
NumberAnimation {
duration: root.animationDuration
easing.type: root.animationEasing
}
}
// Shadow effect
@@ -195,14 +205,15 @@ PanelWindow {
shadowColor: Qt.rgba(0, 0, 0, 0.3)
shadowOpacity: 0.3
}
}
// Keyboard handling
FocusScope {
id: focusScope
anchors.fill: parent
visible: root.visible // Only active when the modal is visible
Keys.onEscapePressed: (event) => {
if (root.closeOnEscapeKey) {
root.visible = false;
@@ -211,16 +222,4 @@ PanelWindow {
}
}
// Convenience functions
function open() {
visible = true
}
function close() {
visible = false
}
function toggle() {
visible = !visible
}
}
}

View File

@@ -13,13 +13,12 @@ Item {
property bool enabled: true
property string unit: "%"
property bool showValue: true
property bool isDragging: false
signal sliderValueChanged(int newValue)
signal sliderDragFinished(int finalValue)
height: 80
property bool isDragging: false
Column {
anchors.fill: parent

View File

@@ -18,6 +18,7 @@ Item {
Row {
id: tabRow
anchors.fill: parent
spacing: tabBar.spacing
@@ -30,19 +31,14 @@ Item {
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
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"
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
@@ -62,16 +58,18 @@ Item {
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)
tabBar.currentIndex = index;
tabBar.tabClicked(index);
}
}
@@ -80,8 +78,13 @@ Item {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
}
}
}

View File

@@ -18,6 +18,7 @@ Item {
Rectangle {
id: background
anchors.fill: parent
radius: toggle.text ? Theme.cornerRadius : 0
color: toggle.text ? (toggleArea.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.08)) : "transparent"
@@ -26,6 +27,7 @@ Item {
Row {
id: textRow
anchors.left: parent.left
anchors.right: toggleTrack.left
anchors.verticalCenter: parent.verticalCenter
@@ -53,11 +55,14 @@ Item {
width: Math.min(implicitWidth, toggle.width - 120)
visible: toggle.description.length > 0
}
}
}
Rectangle {
id: toggleTrack
width: toggle.text ? 48 : parent.width
height: toggle.text ? 24 : parent.height
anchors.right: parent.right
@@ -69,6 +74,7 @@ Item {
Rectangle {
id: toggleHandle
width: 20
height: 20
radius: 10
@@ -76,13 +82,6 @@ Item {
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
@@ -93,12 +92,22 @@ Item {
border.width: 1
z: -1
}
Behavior on x {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.emphasizedEasing
}
}
}
}
MouseArea {
id: toggleArea
anchors.fill: toggle.text ? toggle : toggleTrack
hoverEnabled: true
cursorShape: toggle.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
@@ -110,4 +119,4 @@ Item {
}
}
}
}

View File

@@ -10,24 +10,29 @@ IconImage {
property string colorOverride: ""
property real brightnessOverride: 0.5
property real contrastOverride: 1.0
property real contrastOverride: 1
smooth: true
asynchronous: true
layer.enabled: colorOverride !== ""
Process {
running: true
command: ["sh", "-c", ". /etc/os-release && echo $LOGO"]
stdout: StdioCollector {
onStreamFinished: () => {
root.source = Quickshell.iconPath(this.text.trim());
}
}
}
layer.effect: MultiEffect {
colorization: 1
colorizationColor: colorOverride
brightness: brightnessOverride
contrast: contrastOverride
}
Process {
running: true
command: ["sh", "-c", ". /etc/os-release && echo $LOGO"]
stdout: StdioCollector {
onStreamFinished: () => {
root.source = Quickshell.iconPath(this.text.trim());
}
}
}
}
}