Settings: better horizontal dividers

This commit is contained in:
quadbyte 2025-08-06 20:03:41 -04:00
parent 7c2d2b4d66
commit 63eeb40c64
6 changed files with 980 additions and 856 deletions

View file

@ -1,26 +1,24 @@
import Quickshell
import Quickshell.Wayland
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import qs.Components
import qs.Settings import qs.Settings
import qs.Widgets.SettingsWindow.Tabs import qs.Widgets.SettingsWindow.Tabs
import qs.Widgets.SettingsWindow.Tabs.Components import qs.Widgets.SettingsWindow.Tabs.Components
import qs.Components
PanelWithOverlay { PanelWithOverlay {
id: panelMain id: panelMain
property int activeTabIndex: 0 property int activeTabIndex: 0
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
// Function to show wallpaper selector // Function to show wallpaper selector
function showWallpaperSelector() { function showWallpaperSelector() {
if (wallpaperSelector) { if (wallpaperSelector)
wallpaperSelector.show(); wallpaperSelector.show();
}
} }
// Function to show settings window // Function to show settings window
@ -28,129 +26,138 @@ PanelWithOverlay {
show(); show();
} }
// Handle activeTabIndex changes
onActiveTabIndexChanged: {
if (activeTabIndex >= 0 && activeTabIndex <= 8) {
loadComponentForTab(activeTabIndex);
}
}
// Function to load component for a specific tab // Function to load component for a specific tab
function loadComponentForTab(tabIndex) { function loadComponentForTab(tabIndex) {
const componentMap = { const componentMap = {
0: generalSettings, "0": generalSettings,
1: barSettings, "1": barSettings,
2: timeWeatherSettings, "2": timeWeatherSettings,
3: recordingSettings, "3": recordingSettings,
4: networkSettings, "4": networkSettings,
5: displaySettings, "5": displaySettings,
6: wallpaperSettings, "6": wallpaperSettings,
7: miscSettings, "7": miscSettings,
8: aboutSettings "8": aboutSettings
}; };
const tabNames = ["General", "Bar", "Time & Weather", "Screen Recorder", "Network", "Display", "Wallpaper", "Misc", "About"];
const tabNames = [
"General",
"Bar",
"Time & Weather",
"Screen Recorder",
"Network",
"Display",
"Wallpaper",
"Misc",
"About"
];
if (componentMap[tabIndex]) { if (componentMap[tabIndex]) {
settingsLoader.sourceComponent = componentMap[tabIndex]; settingsLoader.sourceComponent = componentMap[tabIndex];
if (tabName) { if (tabName)
tabName.text = tabNames[tabIndex]; tabName.text = tabNames[tabIndex];
}
} }
} }
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
// Handle activeTabIndex changes
onActiveTabIndexChanged: {
if (activeTabIndex >= 0 && activeTabIndex <= 8)
loadComponentForTab(activeTabIndex);
}
// Add safety checks for component loading // Add safety checks for component loading
Component.onCompleted: { Component.onCompleted: {
// Ensure we start with a valid tab // Ensure we start with a valid tab
if (activeTabIndex < 0 || activeTabIndex > 8) { if (activeTabIndex < 0 || activeTabIndex > 8)
activeTabIndex = 0; activeTabIndex = 0;
}
}
}
// Cleanup when window is hidden // Cleanup when window is hidden
onVisibleChanged: { onVisibleChanged: {
if (!visible) { if (!visible) {
// Reset to default tab when hiding to prevent state issues // Reset to default tab when hiding to prevent state issues
activeTabIndex = 0; activeTabIndex = 0;
if (tabName) { if (tabName)
tabName.text = "General"; tabName.text = "General";
}
} }
} }
Component { Component {
id: generalSettings id: generalSettings
General {}
General {
}
} }
Component { Component {
id: barSettings id: barSettings
Bar {}
Bar {
}
} }
Component { Component {
id: timeWeatherSettings id: timeWeatherSettings
TimeWeather {}
TimeWeather {
}
} }
Component { Component {
id: recordingSettings id: recordingSettings
Recording {}
Recording {
}
} }
Component { Component {
id: networkSettings id: networkSettings
Network {}
Network {
}
} }
Component { Component {
id: miscSettings id: miscSettings
Misc {}
Misc {
}
} }
Component { Component {
id: aboutSettings id: aboutSettings
About {}
About {
}
} }
Component { Component {
id: displaySettings id: displaySettings
Display {}
Display {
}
} }
Component { Component {
id: wallpaperSettings id: wallpaperSettings
Wallpaper {}
Wallpaper {
}
} }
Rectangle { Rectangle {
id: settingsWindowRect id: settingsWindowRect
implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600
implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400
visible: parent.visible visible: parent.visible
color: "transparent" color: "transparent"
// Center the settings window on screen // Center the settings window on screen
anchors.centerIn: parent anchors.centerIn: parent
Rectangle { Rectangle {
id: background id: background
color: Theme.backgroundPrimary color: Theme.backgroundPrimary
anchors.fill: parent anchors.fill: parent
radius: 20 radius: 20
@ -167,11 +174,16 @@ PanelWithOverlay {
shadowVerticalOffset: 2 shadowVerticalOffset: 2
shadowBlur: 12 shadowBlur: 12
} }
} }
Rectangle { Rectangle {
id: settings id: settings
color: Theme.backgroundPrimary color: Theme.backgroundPrimary
topRightRadius: 20
bottomRightRadius: 20
anchors { anchors {
left: tabs.right left: tabs.right
top: parent.top top: parent.top
@ -179,19 +191,19 @@ PanelWithOverlay {
right: parent.right right: parent.right
margins: 12 margins: 12
} }
topRightRadius: 20
bottomRightRadius: 20
Rectangle { Rectangle {
id: headerArea id: headerArea
height: 48
color: "transparent"
anchors { anchors {
top: parent.top top: parent.top
left: parent.left left: parent.left
right: parent.right right: parent.right
margins: 16 margins: 16
} }
height: 48
color: "transparent"
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
@ -199,15 +211,8 @@ PanelWithOverlay {
Text { Text {
id: tabName id: tabName
text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" :
activeTabIndex === 1 ? "Bar" : text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General")
activeTabIndex === 2 ? "Time & Weather" :
activeTabIndex === 3 ? "Screen Recorder" :
activeTabIndex === 4 ? "Network" :
activeTabIndex === 5 ? "Display" :
activeTabIndex === 6 ? "Wallpaper" :
activeTabIndex === 7 ? "Misc" :
activeTabIndex === 8 ? "About" : "General")
font.pixelSize: 18 font.pixelSize: 18
font.bold: true font.bold: true
color: Theme.textPrimary color: Theme.textPrimary
@ -243,10 +248,12 @@ PanelWithOverlay {
color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
} }
} }
MouseArea { MouseArea {
id: wallpaperButtonArea id: wallpaperButtonArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -255,6 +262,7 @@ PanelWithOverlay {
wallpaperSelector.show(); wallpaperSelector.show();
} }
} }
} }
Rectangle { Rectangle {
@ -275,29 +283,36 @@ PanelWithOverlay {
MouseArea { MouseArea {
id: closeButtonArea id: closeButtonArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: panelMain.dismiss() onClicked: panelMain.dismiss()
} }
} }
} }
} }
Rectangle { Rectangle {
height: 1
color: Theme.outline
opacity: 0.3
anchors { anchors {
top: headerArea.bottom top: headerArea.bottom
left: parent.left left: parent.left
right: parent.right right: parent.right
margins: 16 margins: 16
} }
height: 1
color: Theme.outline
opacity: 0.3
} }
Item { Item {
id: settingsContainer id: settingsContainer
anchors { anchors {
top: headerArea.bottom top: headerArea.bottom
left: parent.left left: parent.left
@ -310,6 +325,7 @@ PanelWithOverlay {
// Simplified single loader approach // Simplified single loader approach
Loader { Loader {
id: settingsLoader id: settingsLoader
anchors.fill: parent anchors.fill: parent
sourceComponent: generalSettings sourceComponent: generalSettings
} }
@ -317,13 +333,17 @@ PanelWithOverlay {
// Wallpaper Selector Component // Wallpaper Selector Component
WallpaperSelector { WallpaperSelector {
id: wallpaperSelector id: wallpaperSelector
anchors.fill: parent anchors.fill: parent
} }
} }
} }
Rectangle { Rectangle {
id: tabs id: tabs
color: Theme.surface color: Theme.surface
width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100
height: settingsWindowRect.height height: settingsWindowRect.height
@ -340,17 +360,35 @@ PanelWithOverlay {
Repeater { Repeater {
id: repeater id: repeater
model: [
{ icon: "tune", text: "General" }, model: [{
{ icon: "space_dashboard", text: "Bar" }, "icon": "tune",
{ icon: "schedule", text: "Time & Weather" }, "text": "General"
{ icon: "photo_camera", text: "Screen Recorder" }, }, {
{ icon: "wifi", text: "Network" }, "icon": "space_dashboard",
{ icon: "monitor", text: "Display" }, "text": "Bar"
{ icon: "wallpaper", text: "Wallpaper" }, }, {
{ icon: "settings_suggest", text: "Misc" }, "icon": "schedule",
{ icon: "info", text: "About" } "text": "Time & Weather"
] }, {
"icon": "photo_camera",
"text": "Screen Recorder"
}, {
"icon": "wifi",
"text": "Network"
}, {
"icon": "monitor",
"text": "Display"
}, {
"icon": "wallpaper",
"text": "Wallpaper"
}, {
"icon": "settings_suggest",
"text": "Misc"
}, {
"icon": "info",
"text": "About"
}]
delegate: Rectangle { delegate: Rectangle {
width: tabs.width width: tabs.width
@ -363,6 +401,7 @@ PanelWithOverlay {
Rectangle { Rectangle {
id: activeIndicator id: activeIndicator
Layout.leftMargin: 8 Layout.leftMargin: 8
Layout.preferredWidth: 3 Layout.preferredWidth: 3
Layout.preferredHeight: 24 Layout.preferredHeight: 24
@ -370,11 +409,19 @@ PanelWithOverlay {
radius: 2 radius: 2
color: Theme.accentPrimary color: Theme.accentPrimary
opacity: index === activeTabIndex ? 1 : 0 opacity: index === activeTabIndex ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200 } }
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
} }
Label { Label {
id: icon id: icon
text: modelData.icon text: modelData.icon
font.family: "Material Symbols Outlined" font.family: "Material Symbols Outlined"
font.pixelSize: 24 font.pixelSize: 24
@ -390,12 +437,11 @@ PanelWithOverlay {
Label { Label {
id: label id: label
text: modelData.text text: modelData.text
font.pixelSize: 16 font.pixelSize: 16
color: index === activeTabIndex ? Theme.accentPrimary : color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary)
(tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal)
font.weight: index === activeTabIndex ? Font.DemiBold :
(tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal)
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 24 Layout.preferredHeight: 24
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
@ -403,10 +449,12 @@ PanelWithOverlay {
Layout.rightMargin: 16 Layout.rightMargin: 16
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
} }
} }
MouseArea { MouseArea {
id: tabMouseArea id: tabMouseArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@ -424,9 +472,15 @@ PanelWithOverlay {
visible: index < (repeater.count - 1) visible: index < (repeater.count - 1)
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
} }
} }
} }
} }
} }
} }
} }

View file

@ -1,11 +1,11 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects import QtQuick.Effects
import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import qs.Settings
import qs.Components import qs.Components
import qs.Settings
Item { Item {
id: root id: root
@ -15,131 +15,144 @@ Item {
property var contributors: [] property var contributors: []
property string githubDataPath: Settings.settingsDir + "github_data.json" property string githubDataPath: Settings.settingsDir + "github_data.json"
function loadFromFile() {
const now = Date.now();
const data = githubData;
if (!data.timestamp || (now - data.timestamp > 3.6e+06)) {
console.log("[About] Cache expired or missing, fetching new data from GitHub...");
fetchFromGitHub();
return ;
}
console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)");
if (data.version)
root.latestVersion = data.version;
if (data.contributors)
root.contributors = data.contributors;
}
function fetchFromGitHub() {
versionProcess.running = true;
contributorsProcess.running = true;
}
function saveData() {
githubData.timestamp = Date.now();
Qt.callLater(() => {
githubDataFile.writeAdapter();
});
}
Process { Process {
id: currentVersionProcess id: currentVersionProcess
command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"] command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"]
Component.onCompleted: {
running = true;
}
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
const version = text.trim() const version = text.trim();
if (version && version !== "Unknown") { if (version && version !== "Unknown") {
root.currentVersion = version root.currentVersion = version;
} else { } else {
currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"];
currentVersionProcess.running = true;
}
}
}
currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"]
currentVersionProcess.running = true
}
}
}
Component.onCompleted: {
running = true
}
} }
FileView { FileView {
id: githubDataFile id: githubDataFile
path: root.githubDataPath path: root.githubDataPath
blockLoading: true blockLoading: true
printErrors: true printErrors: true
watchChanges: true watchChanges: true
onFileChanged: githubDataFile.reload()
onLoaded: loadFromFile()
onLoadFailed: function(error) {
console.log("GitHub data file doesn't exist yet, creating it...");
githubData.version = "Unknown";
githubData.contributors = [];
githubData.timestamp = 0;
githubDataFile.writeAdapter();
fetchFromGitHub();
}
Component.onCompleted: {
if (path)
reload();
}
JsonAdapter { JsonAdapter {
id: githubData id: githubData
property string version: "Unknown" property string version: "Unknown"
property var contributors: [] property var contributors: []
property double timestamp: 0 property double timestamp: 0
} }
onFileChanged: githubDataFile.reload()
onLoaded: loadFromFile()
onLoadFailed: function(error) {
console.log("GitHub data file doesn't exist yet, creating it...")
githubData.version = "Unknown"
githubData.contributors = []
githubData.timestamp = 0
githubDataFile.writeAdapter()
fetchFromGitHub()
}
Component.onCompleted: if (path) reload()
}
function loadFromFile() {
const now = Date.now()
const data = githubData
if (!data.timestamp || (now - data.timestamp > 3600000)) {
console.log("[About] Cache expired or missing, fetching new data from GitHub...")
fetchFromGitHub()
return
}
console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)")
if (data.version) {
root.latestVersion = data.version
}
if (data.contributors) {
root.contributors = data.contributors
}
} }
Process { Process {
id: versionProcess id: versionProcess
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"] command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"]
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
try { try {
const data = JSON.parse(text) const data = JSON.parse(text);
if (data.tag_name) { if (data.tag_name) {
const version = data.tag_name const version = data.tag_name;
githubData.version = version githubData.version = version;
root.latestVersion = version root.latestVersion = version;
console.log("[About] Latest version fetched from GitHub:", version) console.log("[About] Latest version fetched from GitHub:", version);
} else { } else {
console.log("No tag_name in GitHub response") console.log("No tag_name in GitHub response");
} }
saveData() saveData();
} catch (e) { } catch (e) {
console.error("Failed to parse version:", e) console.error("Failed to parse version:", e);
} }
} }
} }
} }
Process { Process {
id: contributorsProcess id: contributorsProcess
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"] command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"]
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
try { try {
const data = JSON.parse(text) const data = JSON.parse(text);
githubData.contributors = data || [] githubData.contributors = data || [];
root.contributors = githubData.contributors root.contributors = githubData.contributors;
console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors") console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors");
saveData() saveData();
} catch (e) { } catch (e) {
console.error("Failed to parse contributors:", e) console.error("Failed to parse contributors:", e);
root.contributors = [] root.contributors = [];
}
} }
} }
} }
function fetchFromGitHub() {
versionProcess.running = true
contributorsProcess.running = true
}
function saveData() {
githubData.timestamp = Date.now()
Qt.callLater(() => {
githubDataFile.writeAdapter()
})
} }
Item { Item {
anchors.fill: parent anchors.fill: parent
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.top: parent.top anchors.top: parent.top
@ -147,7 +160,7 @@ Item {
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 32 Layout.preferredHeight: 16
} }
Text { Text {
@ -191,8 +204,8 @@ Item {
color: Theme.textPrimary color: Theme.textPrimary
font.bold: true font.bold: true
} }
}
}
Rectangle { Rectangle {
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
@ -204,20 +217,22 @@ Item {
border.color: Theme.accentPrimary border.color: Theme.accentPrimary
border.width: 1 border.width: 1
visible: { visible: {
if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") { if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown")
return false return false;
}
const latest = root.latestVersion.replace("v", "").split(".")
const current = root.currentVersion.replace("v", "").split(".")
const latest = root.latestVersion.replace("v", "").split(".");
const current = root.currentVersion.replace("v", "").split(".");
for (let i = 0; i < Math.max(latest.length, current.length); i++) { for (let i = 0; i < Math.max(latest.length, current.length); i++) {
const l = parseInt(latest[i] || "0") const l = parseInt(latest[i] || "0");
const c = parseInt(current[i] || "0") const c = parseInt(current[i] || "0");
if (l > c) return true if (l > c)
if (l < c) return false return true;
if (l < c)
return false;
} }
return false return false;
} }
RowLayout { RowLayout {
@ -233,21 +248,25 @@ Item {
Text { Text {
id: updateText id: updateText
text: "Download latest release" text: "Download latest release"
font.pixelSize: 14 font.pixelSize: 14
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
} }
} }
MouseArea { MouseArea {
id: updateArea id: updateArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]) Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]);
} }
} }
} }
Text { Text {
@ -258,10 +277,17 @@ Item {
Layout.topMargin: 24 Layout.topMargin: 24
} }
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
height: 1
color: Theme.outline
opacity: 0.3
}
ColumnLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 32 Layout.leftMargin: 32
Layout.rightMargin: 32 Layout.rightMargin: 32
spacing: 16 spacing: 16
@ -282,6 +308,7 @@ Item {
font.pixelSize: 14 font.pixelSize: 14
color: Theme.textSecondary color: Theme.textSecondary
} }
} }
ScrollView { ScrollView {
@ -294,11 +321,12 @@ Item {
GridView { GridView {
id: contributorsGrid id: contributorsGrid
anchors.centerIn: parent anchors.centerIn: parent
width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200) width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200)
height: parent.height height: parent.height
cellWidth: 200 cellWidth: 200
cellHeight: 110 cellHeight: 100
model: root.contributors model: root.contributors
delegate: Rectangle { delegate: Rectangle {
@ -312,7 +340,6 @@ Item {
anchors.margins: 8 anchors.margins: 8
spacing: 12 spacing: 12
Item { Item {
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 40 Layout.preferredWidth: 40
@ -320,6 +347,7 @@ Item {
Image { Image {
id: avatarImage id: avatarImage
anchors.fill: parent anchors.fill: parent
source: modelData.avatar_url || "" source: modelData.avatar_url || ""
sourceSize: Qt.size(80, 80) sourceSize: Qt.size(80, 80)
@ -340,15 +368,17 @@ Item {
Item { Item {
id: mask id: mask
anchors.fill: parent anchors.fill: parent
layer.enabled: true layer.enabled: true
visible: false visible: false
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: avatarImage.width / 2 radius: avatarImage.width / 2
} }
}
}
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
@ -358,8 +388,8 @@ Item {
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
visible: !avatarImage.source || avatarImage.status !== Image.Ready visible: !avatarImage.source || avatarImage.status !== Image.Ready
} }
}
}
ColumnLayout { ColumnLayout {
spacing: 4 spacing: 4
@ -379,27 +409,36 @@ Item {
font.pixelSize: 11 font.pixelSize: 11
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary
} }
} }
} }
MouseArea { MouseArea {
id: contributorArea id: contributorArea
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onClicked: { onClicked: {
if (modelData.html_url) { if (modelData.html_url)
Quickshell.execDetached(["xdg-open", modelData.html_url]) Quickshell.execDetached(["xdg-open", modelData.html_url]);
}
}
}
}
}
}
}
}
} }
} }
} }
}
}
}
}
}
}
}

View file

@ -1,11 +1,12 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Settings
import qs.Components import qs.Components
import qs.Settings
ColumnLayout { ColumnLayout {
id: root id: root
spacing: 0 spacing: 0
anchors.fill: parent anchors.fill: parent
anchors.margins: 0 anchors.margins: 0
@ -15,7 +16,6 @@ ColumnLayout {
Layout.preferredHeight: 0 Layout.preferredHeight: 0
} }
ColumnLayout { ColumnLayout {
spacing: 4 spacing: 4
Layout.fillWidth: true Layout.fillWidth: true
@ -28,7 +28,6 @@ ColumnLayout {
Layout.bottomMargin: 8 Layout.bottomMargin: 8
} }
ColumnLayout { ColumnLayout {
spacing: 2 spacing: 2
Layout.fillWidth: true Layout.fillWidth: true
@ -67,7 +66,9 @@ ColumnLayout {
z: 2 z: 2
} }
Avatar {} Avatar {
}
} }
Rectangle { Rectangle {
@ -80,6 +81,7 @@ ColumnLayout {
TextInput { TextInput {
id: profileImageInput id: profileImageInput
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: 12 anchors.leftMargin: 12
anchors.rightMargin: 12 anchors.rightMargin: 12
@ -96,25 +98,27 @@ ColumnLayout {
onTextChanged: { onTextChanged: {
Settings.settings.profileImage = text; Settings.settings.profileImage = text;
} }
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.IBeamCursor cursorShape: Qt.IBeamCursor
onClicked: profileImageInput.forceActiveFocus() onClicked: profileImageInput.forceActiveFocus()
} }
} }
} }
} }
} }
} }
Rectangle { Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26 Layout.topMargin: 26
Layout.bottomMargin: 18 Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.3 opacity: 0.3
@ -124,7 +128,6 @@ ColumnLayout {
spacing: 4 spacing: 4
Layout.fillWidth: true Layout.fillWidth: true
Text { Text {
text: "User Interface" text: "User Interface"
font.pixelSize: 18 font.pixelSize: 18
@ -133,7 +136,6 @@ ColumnLayout {
Layout.bottomMargin: 8 Layout.bottomMargin: 8
} }
ColumnLayout { ColumnLayout {
spacing: 4 spacing: 4
Layout.fillWidth: true Layout.fillWidth: true
@ -160,10 +162,12 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
Rectangle { Rectangle {
id: cornersSwitch id: cornersSwitch
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
@ -173,6 +177,7 @@ ColumnLayout {
Rectangle { Rectangle {
id: cornersThumb id: cornersThumb
width: 28 width: 28
height: 28 height: 28
radius: 14 radius: 14
@ -187,7 +192,9 @@ ColumnLayout {
duration: 200 duration: 200
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
} }
MouseArea { MouseArea {
@ -197,10 +204,12 @@ ColumnLayout {
Settings.settings.showCorners = !Settings.settings.showCorners; Settings.settings.showCorners = !Settings.settings.showCorners;
} }
} }
}
}
} }
}
}
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
@ -229,10 +238,12 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
Rectangle { Rectangle {
id: dockSwitch id: dockSwitch
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
@ -242,6 +253,7 @@ ColumnLayout {
Rectangle { Rectangle {
id: dockThumb id: dockThumb
width: 28 width: 28
height: 28 height: 28
radius: 14 radius: 14
@ -256,7 +268,9 @@ ColumnLayout {
duration: 200 duration: 200
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
} }
MouseArea { MouseArea {
@ -266,10 +280,12 @@ ColumnLayout {
Settings.settings.showDock = !Settings.settings.showDock; Settings.settings.showDock = !Settings.settings.showDock;
} }
} }
}
}
} }
}
}
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
@ -298,10 +314,12 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
Rectangle { Rectangle {
id: dimSwitch id: dimSwitch
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
@ -311,6 +329,7 @@ ColumnLayout {
Rectangle { Rectangle {
id: dimThumb id: dimThumb
width: 28 width: 28
height: 28 height: 28
radius: 14 radius: 14
@ -325,7 +344,9 @@ ColumnLayout {
duration: 200 duration: 200
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
} }
MouseArea { MouseArea {
@ -335,13 +356,18 @@ ColumnLayout {
Settings.settings.dimPanels = !Settings.settings.dimPanels; Settings.settings.dimPanels = !Settings.settings.dimPanels;
} }
} }
} }
} }
} }
} }
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
} }
} }

View file

@ -98,19 +98,14 @@ ColumnLayout {
} }
Rectangle { Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26 Layout.topMargin: 26
Layout.bottomMargin: 18 Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.3 opacity: 0.3
} }
ColumnLayout { ColumnLayout {
spacing: 16 spacing: 16
Layout.fillWidth: true Layout.fillWidth: true

View file

@ -1,12 +1,13 @@
import QtQuick import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Settings
import qs.Components import qs.Components
import qs.Settings
import qs.Widgets.SettingsWindow.Tabs.Components import qs.Widgets.SettingsWindow.Tabs.Components
ColumnLayout { ColumnLayout {
id: root id: root
spacing: 0 spacing: 0
anchors.fill: parent anchors.fill: parent
anchors.margins: 0 anchors.margins: 0
@ -16,7 +17,6 @@ ColumnLayout {
Layout.preferredHeight: 0 Layout.preferredHeight: 0
} }
ColumnLayout { ColumnLayout {
spacing: 4 spacing: 4
Layout.fillWidth: true Layout.fillWidth: true
@ -29,7 +29,6 @@ ColumnLayout {
Layout.bottomMargin: 8 Layout.bottomMargin: 8
} }
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
@ -57,10 +56,12 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
Rectangle { Rectangle {
id: use12HourClockSwitch id: use12HourClockSwitch
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
@ -70,6 +71,7 @@ ColumnLayout {
Rectangle { Rectangle {
id: use12HourClockThumb id: use12HourClockThumb
width: 28 width: 28
height: 28 height: 28
radius: 14 radius: 14
@ -84,7 +86,9 @@ ColumnLayout {
duration: 200 duration: 200
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
} }
MouseArea { MouseArea {
@ -94,10 +98,12 @@ ColumnLayout {
Settings.settings.use12HourClock = !Settings.settings.use12HourClock; Settings.settings.use12HourClock = !Settings.settings.use12HourClock;
} }
} }
}
}
} }
}
}
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
@ -126,10 +132,12 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
Rectangle { Rectangle {
id: reverseDayMonthSwitch id: reverseDayMonthSwitch
width: 52 width: 52
height: 32 height: 32
radius: 16 radius: 16
@ -139,6 +147,7 @@ ColumnLayout {
Rectangle { Rectangle {
id: reverseDayMonthThumb id: reverseDayMonthThumb
width: 28 width: 28
height: 28 height: 28
radius: 14 radius: 14
@ -153,7 +162,9 @@ ColumnLayout {
duration: 200 duration: 200
easing.type: Easing.OutCubic easing.type: Easing.OutCubic
} }
} }
} }
MouseArea { MouseArea {
@ -163,25 +174,24 @@ ColumnLayout {
Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth;
} }
} }
} }
} }
} }
} }
Rectangle { Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26 Layout.topMargin: 26
Layout.bottomMargin: 18 Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.3 opacity: 0.3
} }
ColumnLayout { ColumnLayout {
spacing: 4 spacing: 4
Layout.fillWidth: true Layout.fillWidth: true
@ -194,7 +204,6 @@ ColumnLayout {
Layout.bottomMargin: 8 Layout.bottomMargin: 8
} }
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
@ -223,6 +232,7 @@ ColumnLayout {
TextInput { TextInput {
id: cityInput id: cityInput
anchors.fill: parent anchors.fill: parent
anchors.leftMargin: 12 anchors.leftMargin: 12
anchors.rightMargin: 12 anchors.rightMargin: 12
@ -237,7 +247,6 @@ ColumnLayout {
selectByMouse: true selectByMouse: true
activeFocusOnTab: true activeFocusOnTab: true
inputMethodHints: Qt.ImhNone inputMethodHints: Qt.ImhNone
onTextChanged: { onTextChanged: {
Settings.settings.weatherCity = text; Settings.settings.weatherCity = text;
} }
@ -249,10 +258,12 @@ ColumnLayout {
cityInput.forceActiveFocus(); cityInput.forceActiveFocus();
} }
} }
}
}
} }
}
}
ColumnLayout { ColumnLayout {
spacing: 8 spacing: 8
@ -281,15 +292,21 @@ ColumnLayout {
wrapMode: Text.WordWrap wrapMode: Text.WordWrap
Layout.fillWidth: true Layout.fillWidth: true
} }
} }
UnitSelector {} UnitSelector {
} }
} }
}
} }
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
} }
} }

View file

@ -122,16 +122,15 @@ ColumnLayout {
} }
} }
}
} }
Rectangle { Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26 Layout.topMargin: 26
Layout.bottomMargin: 18 Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.3 opacity: 0.3
@ -388,16 +387,10 @@ ColumnLayout {
} }
}
Rectangle { Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26 Layout.topMargin: 26
Layout.bottomMargin: 18 Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1 height: 1
color: Theme.outline color: Theme.outline
opacity: 0.3 opacity: 0.3