Settings rework...
This commit is contained in:
parent
74b233798d
commit
fb68300746
63 changed files with 7139 additions and 1026 deletions
369
Widgets/SettingsWindow/SettingsWindow.qml
Normal file
369
Widgets/SettingsWindow/SettingsWindow.qml
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import qs.Settings
|
||||
import qs.Widgets.SettingsWindow.Tabs
|
||||
|
||||
PanelWindow {
|
||||
id: panelMain
|
||||
implicitHeight: screen.height / 2
|
||||
implicitWidth: screen.width / 2
|
||||
color: "transparent"
|
||||
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
|
||||
|
||||
Component {
|
||||
id: generalSettings
|
||||
General {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: barSettings
|
||||
Bar {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: timeWeatherSettings
|
||||
TimeWeather {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: recordingSettings
|
||||
Recording {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: networkSettings
|
||||
Network {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: miscSettings
|
||||
Misc {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: aboutSettings
|
||||
About {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: displaySettings
|
||||
Display {}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
color: Theme.backgroundPrimary
|
||||
anchors.fill: parent
|
||||
radius: 20
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
|
||||
MultiEffect {
|
||||
source: background
|
||||
anchors.fill: background
|
||||
shadowEnabled: true
|
||||
shadowColor: Theme.shadow
|
||||
shadowOpacity: 0.3
|
||||
shadowHorizontalOffset: 0
|
||||
shadowVerticalOffset: 2
|
||||
shadowBlur: 12
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: settings
|
||||
color: Theme.backgroundTertiary
|
||||
anchors {
|
||||
left: tabs.right
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: 12
|
||||
}
|
||||
topRightRadius: 20
|
||||
bottomRightRadius: 20
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: headerArea
|
||||
anchors {
|
||||
top: parent.top
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 16
|
||||
}
|
||||
height: 48
|
||||
color: "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
|
||||
|
||||
Text {
|
||||
id: tabName
|
||||
text: "General"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
width: 32
|
||||
height: 32
|
||||
radius: 16
|
||||
color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent"
|
||||
border.color: Theme.accentPrimary
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "close"
|
||||
font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
|
||||
font.pixelSize: 18
|
||||
color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: closeButtonArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: panelMain.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: headerArea.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
margins: 16
|
||||
}
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.3
|
||||
}
|
||||
|
||||
Item {
|
||||
id: settingsContainer
|
||||
anchors {
|
||||
top: headerArea.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
margins: 24
|
||||
topMargin: 32
|
||||
}
|
||||
|
||||
|
||||
Loader {
|
||||
id: settingsLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: generalSettings
|
||||
opacity: 1
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loader {
|
||||
id: settingsLoader2
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
visible: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: tabs
|
||||
color: Theme.surface
|
||||
width: screen.width / 9
|
||||
height: panelMain.height
|
||||
topLeftRadius: 20
|
||||
bottomLeftRadius: 20
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
topPadding: 8
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: [
|
||||
{ icon: "tune", text: "General" },
|
||||
{ icon: "space_dashboard", text: "Bar" },
|
||||
{ icon: "schedule", text: "Time & Weather" },
|
||||
{ icon: "photo_camera", text: "Recording" },
|
||||
{ icon: "wifi", text: "Network" },
|
||||
{ icon: "monitor", text: "Display" },
|
||||
{ icon: "settings_suggest", text: "Misc" },
|
||||
{ icon: "info", text: "About" }
|
||||
]
|
||||
|
||||
delegate: Column {
|
||||
width: tabs.width
|
||||
height: 40
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 39
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 8
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: activeIndicator
|
||||
Layout.leftMargin: 8
|
||||
Layout.preferredWidth: 3
|
||||
Layout.preferredHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
radius: 2
|
||||
color: Theme.accentPrimary
|
||||
opacity: index === 0 ? 1 : 0
|
||||
Behavior on opacity { NumberAnimation { duration: 200 } }
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
id: icon
|
||||
text: modelData.icon
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: index === 0 ? Theme.accentPrimary : Theme.textPrimary
|
||||
opacity: index === 0 ? 1 : 0.8
|
||||
Layout.leftMargin: 20
|
||||
Layout.preferredWidth: 24
|
||||
Layout.preferredHeight: 24
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
|
||||
Label {
|
||||
id: label
|
||||
text: modelData.text
|
||||
font.pixelSize: 12
|
||||
color: index === 0 ? Theme.accentPrimary : Theme.textSecondary
|
||||
font.weight: index === 0 ? Font.DemiBold : Font.Normal
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 16
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: {
|
||||
|
||||
const newComponent = {
|
||||
0: generalSettings,
|
||||
1: barSettings,
|
||||
2: timeWeatherSettings,
|
||||
3: recordingSettings,
|
||||
4: networkSettings,
|
||||
5: displaySettings,
|
||||
6: miscSettings,
|
||||
7: aboutSettings
|
||||
}[index];
|
||||
|
||||
|
||||
const tabNames = [
|
||||
"General",
|
||||
"Bar",
|
||||
"Time & Weather",
|
||||
"Recording",
|
||||
"Network",
|
||||
"Display",
|
||||
"Misc",
|
||||
"About"
|
||||
];
|
||||
tabName.text = tabNames[index];
|
||||
|
||||
|
||||
if (settingsLoader.opacity === 1) {
|
||||
|
||||
settingsLoader2.sourceComponent = newComponent;
|
||||
settingsLoader.opacity = 0;
|
||||
settingsLoader2.opacity = 1;
|
||||
} else {
|
||||
|
||||
settingsLoader.sourceComponent = newComponent;
|
||||
settingsLoader2.opacity = 0;
|
||||
settingsLoader.opacity = 1;
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < repeater.count; i++) {
|
||||
let item = repeater.itemAt(i);
|
||||
if (item) {
|
||||
|
||||
let containerItem = item.children[0];
|
||||
|
||||
let rowLayout = containerItem.children[0];
|
||||
|
||||
let indicator = rowLayout.children[0];
|
||||
let icon = rowLayout.children[1];
|
||||
let label = rowLayout.children[2];
|
||||
|
||||
indicator.opacity = i === index ? 1 : 0;
|
||||
icon.color = i === index ? Theme.accentPrimary : Theme.textPrimary;
|
||||
icon.opacity = i === index ? 1 : 0.8;
|
||||
label.color = i === index ? Theme.accentPrimary : Theme.textSecondary;
|
||||
label.font.weight = i === index ? Font.Bold : Font.Normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.outline
|
||||
opacity: 0.6
|
||||
visible: index < (repeater.count - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
405
Widgets/SettingsWindow/Tabs/About.qml
Normal file
405
Widgets/SettingsWindow/Tabs/About.qml
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string latestVersion: "Unknown"
|
||||
property string currentVersion: "Unknown"
|
||||
property var contributors: []
|
||||
property string githubDataPath: Settings.settingsDir + "github_data.json"
|
||||
|
||||
Process {
|
||||
id: currentVersionProcess
|
||||
command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
const version = text.trim()
|
||||
if (version && version !== "Unknown") {
|
||||
root.currentVersion = version
|
||||
} else {
|
||||
|
||||
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 {
|
||||
id: githubDataFile
|
||||
path: root.githubDataPath
|
||||
blockLoading: true
|
||||
printErrors: true
|
||||
watchChanges: true
|
||||
|
||||
JsonAdapter {
|
||||
id: githubData
|
||||
property string version: "Unknown"
|
||||
property var contributors: []
|
||||
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 {
|
||||
id: versionProcess
|
||||
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const data = JSON.parse(text)
|
||||
if (data.tag_name) {
|
||||
const version = data.tag_name
|
||||
githubData.version = version
|
||||
root.latestVersion = version
|
||||
console.log("[About] Latest version fetched from GitHub:", version)
|
||||
} else {
|
||||
console.log("No tag_name in GitHub response")
|
||||
}
|
||||
saveData()
|
||||
} catch (e) {
|
||||
console.error("Failed to parse version:", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: contributorsProcess
|
||||
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"]
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
try {
|
||||
const data = JSON.parse(text)
|
||||
githubData.contributors = data || []
|
||||
root.contributors = githubData.contributors
|
||||
console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors")
|
||||
saveData()
|
||||
} catch (e) {
|
||||
console.error("Failed to parse contributors:", e)
|
||||
root.contributors = []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fetchFromGitHub() {
|
||||
versionProcess.running = true
|
||||
contributorsProcess.running = true
|
||||
}
|
||||
|
||||
function saveData() {
|
||||
githubData.timestamp = Date.now()
|
||||
Qt.callLater(() => {
|
||||
githubDataFile.writeAdapter()
|
||||
})
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
id: mainLayout
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: 8
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 32
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Noctalia"
|
||||
font.pixelSize: 24
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
columns: 2
|
||||
rowSpacing: 4
|
||||
columnSpacing: 8
|
||||
|
||||
Text {
|
||||
text: "Latest Version:"
|
||||
font.pixelSize: 16
|
||||
color: Theme.textSecondary
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.latestVersion
|
||||
font.pixelSize: 16
|
||||
color: Theme.textPrimary
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Installed Version:"
|
||||
font.pixelSize: 16
|
||||
color: Theme.textSecondary
|
||||
Layout.alignment: Qt.AlignRight
|
||||
}
|
||||
|
||||
Text {
|
||||
text: root.currentVersion
|
||||
font.pixelSize: 16
|
||||
color: Theme.textPrimary
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.topMargin: 8
|
||||
Layout.preferredWidth: updateText.implicitWidth + 46
|
||||
Layout.preferredHeight: 32
|
||||
radius: 20
|
||||
color: updateArea.containsMouse ? Theme.accentPrimary : "transparent"
|
||||
border.color: Theme.accentPrimary
|
||||
border.width: 1
|
||||
visible: {
|
||||
if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") {
|
||||
return false
|
||||
}
|
||||
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++) {
|
||||
const l = parseInt(latest[i] || "0")
|
||||
const c = parseInt(current[i] || "0")
|
||||
if (l > c) return true
|
||||
if (l < c) return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "system_update"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 18
|
||||
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
id: updateText
|
||||
text: "Download latest release"
|
||||
font.pixelSize: 14
|
||||
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: updateArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Description something something <.< I hate writing text..."
|
||||
font.pixelSize: 14
|
||||
color: Theme.textSecondary
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.topMargin: 16
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 32
|
||||
Layout.leftMargin: 32
|
||||
Layout.rightMargin: 32
|
||||
spacing: 16
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
spacing: 8
|
||||
|
||||
Text {
|
||||
text: "Contributors"
|
||||
font.pixelSize: 18
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "(" + root.contributors.length + ")"
|
||||
font.pixelSize: 14
|
||||
color: Theme.textSecondary
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 300
|
||||
clip: true
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
GridView {
|
||||
id: contributorsGrid
|
||||
anchors.centerIn: parent
|
||||
width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200)
|
||||
height: parent.height
|
||||
cellWidth: 200
|
||||
cellHeight: 110
|
||||
model: root.contributors
|
||||
|
||||
delegate: Rectangle {
|
||||
width: contributorsGrid.cellWidth - 4
|
||||
height: contributorsGrid.cellHeight - 10
|
||||
radius: 20
|
||||
color: contributorArea.containsMouse ? Theme.highlight : "transparent"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
spacing: 12
|
||||
|
||||
|
||||
Item {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.preferredWidth: 40
|
||||
Layout.preferredHeight: 40
|
||||
|
||||
Image {
|
||||
id: avatarImage
|
||||
anchors.fill: parent
|
||||
source: modelData.avatar_url || ""
|
||||
sourceSize: Qt.size(80, 80)
|
||||
visible: false
|
||||
mipmap: true
|
||||
smooth: true
|
||||
asynchronous: true
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
cache: true
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: avatarImage
|
||||
maskEnabled: true
|
||||
maskSource: mask
|
||||
}
|
||||
|
||||
Item {
|
||||
id: mask
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
radius: avatarImage.width / 2
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "person"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
|
||||
visible: !avatarImage.source || avatarImage.status !== Image.Ready
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: modelData.login || "Unknown"
|
||||
font.pixelSize: 13
|
||||
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
|
||||
elide: Text.ElideRight
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Text {
|
||||
text: (modelData.contributions || 0) + " commits"
|
||||
font.pixelSize: 11
|
||||
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: contributorArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (modelData.html_url) {
|
||||
Quickshell.execDetached(["xdg-open", modelData.html_url])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
380
Widgets/SettingsWindow/Tabs/Bar.qml
Normal file
380
Widgets/SettingsWindow/Tabs/Bar.qml
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Bar Elements"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Active Window Icon"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display the icon of the currently focused window in the bar"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: activeWindowIconSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: activeWindowIconThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Active Window"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display the title of the currently focused window below the bar"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: activeWindowSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: activeWindowThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showActiveWindow ? activeWindowSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show System Info"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display system information (CPU, RAM, etc.) in the bar"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: systemInfoSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: systemInfoThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Taskbar"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display a taskbar showing currently open windows"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: taskbarSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: taskbarThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showTaskbar = !Settings.settings.showTaskbar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Media"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display media controls and information in the bar"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: mediaSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: mediaThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
97
Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml
Normal file
97
Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
width: 64
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
|
||||
property bool useFahrenheit: Settings.settings.useFahrenheit
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: slider
|
||||
width: parent.width / 2 - 4
|
||||
height: parent.height - 4
|
||||
radius: 14
|
||||
color: Theme.accentPrimary
|
||||
x: 2 + (useFahrenheit ? parent.width / 2 : 0)
|
||||
y: 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
|
||||
Item {
|
||||
width: parent.width / 2
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "°C"
|
||||
font.pixelSize: 13
|
||||
font.bold: !useFahrenheit
|
||||
color: !useFahrenheit ? Theme.onAccent : Theme.textPrimary
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (useFahrenheit) {
|
||||
Settings.settings.useFahrenheit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
width: parent.width / 2
|
||||
height: parent.height
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "°F"
|
||||
font.pixelSize: 13
|
||||
font.bold: useFahrenheit
|
||||
color: useFahrenheit ? Theme.onAccent : Theme.textPrimary
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation { duration: 200 }
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (!useFahrenheit) {
|
||||
Settings.settings.useFahrenheit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
354
Widgets/SettingsWindow/Tabs/Display.qml
Normal file
354
Widgets/SettingsWindow/Tabs/Display.qml
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
// Get list of available monitors/screens
|
||||
property var monitors: Quickshell.screens || []
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Monitor Selection"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Bar Monitors"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Select which monitors to display the top panel/bar on"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: root.monitors
|
||||
delegate: Rectangle {
|
||||
id: barCheckbox
|
||||
property bool isChecked: false
|
||||
|
||||
Component.onCompleted: {
|
||||
// Initialize checkbox state from settings
|
||||
let monitors = Settings.settings.barMonitors || [];
|
||||
isChecked = monitors.includes(modelData.name);
|
||||
}
|
||||
|
||||
width: checkboxContent.implicitWidth + 16
|
||||
height: 32
|
||||
radius: 16
|
||||
color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: isChecked ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: checkboxContent
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: barCheckbox.isChecked ? "check" : ""
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 14
|
||||
color: barCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary
|
||||
visible: barCheckbox.isChecked
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.name || "Unknown"
|
||||
font.pixelSize: 12
|
||||
color: barCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
isChecked = !isChecked;
|
||||
|
||||
// Update settings array when checkbox is toggled
|
||||
let monitors = Settings.settings.barMonitors || [];
|
||||
monitors = [...monitors]; // Create copy to trigger reactivity
|
||||
|
||||
if (isChecked) {
|
||||
if (!monitors.includes(modelData.name)) {
|
||||
monitors.push(modelData.name);
|
||||
}
|
||||
} else {
|
||||
monitors = monitors.filter(name => name !== modelData.name);
|
||||
}
|
||||
|
||||
Settings.settings.barMonitors = monitors;
|
||||
console.log("Bar monitors updated:", JSON.stringify(monitors));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Dock Monitors"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Select which monitors to display the application dock on"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: root.monitors
|
||||
delegate: Rectangle {
|
||||
id: dockCheckbox
|
||||
property bool isChecked: false
|
||||
|
||||
Component.onCompleted: {
|
||||
// Initialize with current settings
|
||||
let monitors = Settings.settings.dockMonitors || [];
|
||||
isChecked = monitors.includes(modelData.name);
|
||||
}
|
||||
|
||||
width: checkboxContent.implicitWidth + 16
|
||||
height: 32
|
||||
radius: 16
|
||||
color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: isChecked ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: checkboxContent
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: dockCheckbox.isChecked ? "check" : ""
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 14
|
||||
color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary
|
||||
visible: dockCheckbox.isChecked
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.name || "Unknown"
|
||||
font.pixelSize: 12
|
||||
color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
// Toggle state immediately for UI responsiveness
|
||||
isChecked = !isChecked;
|
||||
|
||||
// Update settings
|
||||
let monitors = Settings.settings.dockMonitors || [];
|
||||
monitors = [...monitors]; // Copy array
|
||||
|
||||
if (isChecked) {
|
||||
// Add to array if not already there
|
||||
if (!monitors.includes(modelData.name)) {
|
||||
monitors.push(modelData.name);
|
||||
}
|
||||
} else {
|
||||
// Remove from array
|
||||
monitors = monitors.filter(name => name !== modelData.name);
|
||||
}
|
||||
|
||||
Settings.settings.dockMonitors = monitors;
|
||||
console.log("Dock monitors updated:", JSON.stringify(monitors));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Notification Monitors"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Select which monitors to display system notifications on"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Flow {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Repeater {
|
||||
model: root.monitors
|
||||
delegate: Rectangle {
|
||||
id: notificationCheckbox
|
||||
property bool isChecked: false
|
||||
|
||||
Component.onCompleted: {
|
||||
// Initialize with current settings
|
||||
let monitors = Settings.settings.notificationMonitors || [];
|
||||
isChecked = monitors.includes(modelData.name);
|
||||
}
|
||||
|
||||
width: checkboxContent.implicitWidth + 16
|
||||
height: 32
|
||||
radius: 16
|
||||
color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: isChecked ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
RowLayout {
|
||||
id: checkboxContent
|
||||
anchors.centerIn: parent
|
||||
spacing: 6
|
||||
|
||||
Text {
|
||||
text: notificationCheckbox.isChecked ? "check" : ""
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 14
|
||||
color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary
|
||||
visible: notificationCheckbox.isChecked
|
||||
}
|
||||
|
||||
Text {
|
||||
text: modelData.name || "Unknown"
|
||||
font.pixelSize: 12
|
||||
color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
// Toggle state immediately for UI responsiveness
|
||||
isChecked = !isChecked;
|
||||
|
||||
// Update settings
|
||||
let monitors = Settings.settings.notificationMonitors || [];
|
||||
monitors = [...monitors]; // Copy array
|
||||
|
||||
if (isChecked) {
|
||||
// Add to array if not already there
|
||||
if (!monitors.includes(modelData.name)) {
|
||||
monitors.push(modelData.name);
|
||||
}
|
||||
} else {
|
||||
// Remove from array
|
||||
monitors = monitors.filter(name => name !== modelData.name);
|
||||
}
|
||||
|
||||
Settings.settings.notificationMonitors = monitors;
|
||||
console.log("Notification monitors updated:", JSON.stringify(monitors));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
339
Widgets/SettingsWindow/Tabs/General.qml
Normal file
339
Widgets/SettingsWindow/Tabs/General.qml
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Profile"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Profile Image"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Your profile picture displayed in various places throughout the shell"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
width: 48
|
||||
height: 48
|
||||
radius: 24
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "transparent"
|
||||
radius: 24
|
||||
border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
z: 2
|
||||
}
|
||||
|
||||
Avatar {}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: 16
|
||||
color: Theme.surfaceVariant
|
||||
border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
TextInput {
|
||||
id: profileImageInput
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
anchors.topMargin: 6
|
||||
anchors.bottomMargin: 6
|
||||
text: Settings.settings.profileImage
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
clip: true
|
||||
selectByMouse: true
|
||||
activeFocusOnTab: true
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
onTextChanged: {
|
||||
Settings.settings.profileImage = text;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: profileImageInput.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 16
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "User Interface"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Corners"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display rounded corners on screen edges"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cornersSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: cornersThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showCorners = !Settings.settings.showCorners;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Dock"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display a dock at the bottom of the screen for quick access to applications"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dockSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: dockThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showDock ? dockSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showDock = !Settings.settings.showDock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Dim Desktop"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Dim the desktop when panels or menus are open"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: dimSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: dimThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.dimPanels = !Settings.settings.dimPanels;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
137
Widgets/SettingsWindow/Tabs/Misc.qml
Normal file
137
Widgets/SettingsWindow/Tabs/Misc.qml
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Media"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Visualizer Type"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Choose the style of the audio visualizer"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: visualizerTypeComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["radial", "fire", "diamond"]
|
||||
currentIndex: model.indexOf(Settings.settings.visualizerType)
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing
|
||||
text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1)
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: visualizerTypeComboBox.width - width - 12
|
||||
y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: visualizerTypeComboBox.height
|
||||
width: visualizerTypeComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null
|
||||
currentIndex: visualizerTypeComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: visualizerTypeComboBox.width
|
||||
contentItem: Text {
|
||||
text: modelData.charAt(0).toUpperCase() + modelData.slice(1)
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: visualizerTypeComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.visualizerType = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
193
Widgets/SettingsWindow/Tabs/Network.qml
Normal file
193
Widgets/SettingsWindow/Tabs/Network.qml
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 24
|
||||
|
||||
Component.onCompleted: {
|
||||
|
||||
Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"])
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 16
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Wi-Fi"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Enable Wi-Fi"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Turn Wi-Fi radio on or off"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: wifiSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
property bool checked: Settings.settings.wifiEnabled
|
||||
color: checked ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: checked ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: wifiThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: wifiSwitch.checked ? wifiSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled
|
||||
Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 16
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
Text {
|
||||
text: "Bluetooth"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Enable Bluetooth"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Turn Bluetooth radio on or off"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: bluetoothSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
property bool checked: Settings.settings.bluetoothEnabled
|
||||
color: checked ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: checked ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: bluetoothThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: bluetoothSwitch.checked ? bluetoothSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
if (Bluetooth.defaultAdapter) {
|
||||
Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled
|
||||
Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled
|
||||
if (Bluetooth.defaultAdapter.enabled) {
|
||||
Bluetooth.defaultAdapter.discovering = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
19
Widgets/SettingsWindow/Tabs/Record.qml
Normal file
19
Widgets/SettingsWindow/Tabs/Record.qml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 24
|
||||
|
||||
Text {
|
||||
text: "Coming soon..."
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
Layout.topMargin: 32
|
||||
}
|
||||
}
|
||||
812
Widgets/SettingsWindow/Tabs/Recording.qml
Normal file
812
Widgets/SettingsWindow/Tabs/Recording.qml
Normal file
|
|
@ -0,0 +1,812 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
padding: 0
|
||||
rightPadding: 12
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical.policy: ScrollBar.AsNeeded
|
||||
|
||||
ColumnLayout {
|
||||
width: scrollView.availableWidth
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Screen Recording"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Output Directory"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Directory where screen recordings will be saved"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: 16
|
||||
color: Theme.surfaceVariant
|
||||
border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
TextInput {
|
||||
id: videoPathInput
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
anchors.topMargin: 6
|
||||
anchors.bottomMargin: 6
|
||||
text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : ""
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
clip: true
|
||||
selectByMouse: true
|
||||
activeFocusOnTab: true
|
||||
inputMethodHints: Qt.ImhUrlCharactersOnly
|
||||
onTextChanged: {
|
||||
Settings.settings.videoPath = text;
|
||||
}
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: videoPathInput.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Frame Rate"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Target frame rate for screen recordings (default: 60)"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: frameRateSpinBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
from: 24
|
||||
to: 144
|
||||
value: Settings.settings.recordingFrameRate || 60
|
||||
stepSize: 1
|
||||
|
||||
onValueChanged: {
|
||||
Settings.settings.recordingFrameRate = value;
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: frameRateSpinBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: TextInput {
|
||||
text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale)
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
selectionColor: Theme.accentPrimary
|
||||
selectedTextColor: Theme.onAccent
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
readOnly: false
|
||||
selectByMouse: true
|
||||
validator: IntValidator {
|
||||
bottom: frameRateSpinBox.from
|
||||
top: frameRateSpinBox.to
|
||||
}
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
|
||||
onTextChanged: {
|
||||
var newValue = parseInt(text);
|
||||
if (!isNaN(newValue) && newValue >= frameRateSpinBox.from && newValue <= frameRateSpinBox.to) {
|
||||
frameRateSpinBox.value = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
onEditingFinished: {
|
||||
var newValue = parseInt(text);
|
||||
if (isNaN(newValue) || newValue < frameRateSpinBox.from || newValue > frameRateSpinBox.to) {
|
||||
text = frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
up.indicator: Rectangle {
|
||||
x: parent.width - width
|
||||
height: parent.height
|
||||
width: height
|
||||
color: "transparent"
|
||||
radius: 16
|
||||
|
||||
Text {
|
||||
text: "add"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 20
|
||||
color: Theme.textPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
|
||||
down.indicator: Rectangle {
|
||||
x: 0
|
||||
height: parent.height
|
||||
width: height
|
||||
color: "transparent"
|
||||
radius: 16
|
||||
|
||||
Text {
|
||||
text: "remove"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 20
|
||||
color: Theme.textPrimary
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Audio Source"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Audio source to capture during recording"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: audioSourceComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["default_output", "default_input", "both"]
|
||||
currentIndex: model.indexOf(Settings.settings.recordingAudioSource || "default_output")
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: audioSourceComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: audioSourceComboBox.indicator.width + audioSourceComboBox.spacing
|
||||
text: {
|
||||
switch(audioSourceComboBox.currentText) {
|
||||
case "default_output": return "System Audio";
|
||||
case "default_input": return "Microphone";
|
||||
case "both": return "System Audio + Microphone";
|
||||
default: return audioSourceComboBox.currentText;
|
||||
}
|
||||
}
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: audioSourceComboBox.width - width - 12
|
||||
y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: audioSourceComboBox.height
|
||||
width: audioSourceComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: audioSourceComboBox.popup.visible ? audioSourceComboBox.delegateModel : null
|
||||
currentIndex: audioSourceComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: audioSourceComboBox.width
|
||||
contentItem: Text {
|
||||
text: {
|
||||
switch(modelData) {
|
||||
case "default_output": return "System Audio";
|
||||
case "default_input": return "Microphone";
|
||||
case "both": return "System Audio + Microphone";
|
||||
default: return modelData;
|
||||
}
|
||||
}
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: audioSourceComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.recordingAudioSource = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Video Quality"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Higher quality results in larger file sizes"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: qualityComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["medium", "high", "very_high", "ultra"]
|
||||
currentIndex: model.indexOf(Settings.settings.recordingQuality || "very_high")
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: qualityComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: qualityComboBox.indicator.width + qualityComboBox.spacing
|
||||
text: {
|
||||
switch(qualityComboBox.currentText) {
|
||||
case "medium": return "Medium";
|
||||
case "high": return "High";
|
||||
case "very_high": return "Very High";
|
||||
case "ultra": return "Ultra";
|
||||
default: return qualityComboBox.currentText;
|
||||
}
|
||||
}
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: qualityComboBox.width - width - 12
|
||||
y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: qualityComboBox.height
|
||||
width: qualityComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: qualityComboBox.popup.visible ? qualityComboBox.delegateModel : null
|
||||
currentIndex: qualityComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: qualityComboBox.width
|
||||
contentItem: Text {
|
||||
text: {
|
||||
switch(modelData) {
|
||||
case "medium": return "Medium";
|
||||
case "high": return "High";
|
||||
case "very_high": return "Very High";
|
||||
case "ultra": return "Ultra";
|
||||
default: return modelData;
|
||||
}
|
||||
}
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: qualityComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.recordingQuality = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Video Codec"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Different codecs offer different compression and compatibility"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: codecComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["h264", "hevc", "av1", "vp8", "vp9"]
|
||||
currentIndex: model.indexOf(Settings.settings.recordingCodec || "h264")
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: codecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: codecComboBox.indicator.width + codecComboBox.spacing
|
||||
text: codecComboBox.currentText.toUpperCase()
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: codecComboBox.width - width - 12
|
||||
y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: codecComboBox.height
|
||||
width: codecComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: codecComboBox.popup.visible ? codecComboBox.delegateModel : null
|
||||
currentIndex: codecComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: codecComboBox.width
|
||||
contentItem: Text {
|
||||
text: modelData.toUpperCase()
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: codecComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.recordingCodec = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Audio Codec"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Opus is recommended for best performance and smallest audio size"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: audioCodecComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["opus", "aac"]
|
||||
currentIndex: model.indexOf(Settings.settings.audioCodec || "opus")
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: audioCodecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing
|
||||
text: audioCodecComboBox.currentText.toUpperCase()
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: audioCodecComboBox.width - width - 12
|
||||
y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: audioCodecComboBox.height
|
||||
width: audioCodecComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: audioCodecComboBox.popup.visible ? audioCodecComboBox.delegateModel : null
|
||||
currentIndex: audioCodecComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: audioCodecComboBox.width
|
||||
contentItem: Text {
|
||||
text: modelData.toUpperCase()
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: audioCodecComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.audioCodec = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
Text {
|
||||
text: "Color Range"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Limited is recommended for better compatibility"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.bottomMargin: 4
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: colorRangeComboBox
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
model: ["limited", "full"]
|
||||
currentIndex: model.indexOf(Settings.settings.colorRange || "limited")
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: 120
|
||||
implicitHeight: 40
|
||||
color: Theme.surfaceVariant
|
||||
border.color: colorRangeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 12
|
||||
rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing
|
||||
text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1)
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Text {
|
||||
x: colorRangeComboBox.width - width - 12
|
||||
y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2
|
||||
text: "arrow_drop_down"
|
||||
font.family: "Material Symbols Outlined"
|
||||
font.pixelSize: 24
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
popup: Popup {
|
||||
y: colorRangeComboBox.height
|
||||
width: colorRangeComboBox.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: colorRangeComboBox.popup.visible ? colorRangeComboBox.delegateModel : null
|
||||
currentIndex: colorRangeComboBox.highlightedIndex
|
||||
|
||||
ScrollIndicator.vertical: ScrollIndicator {}
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: Theme.surfaceVariant
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
radius: 16
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
width: colorRangeComboBox.width
|
||||
contentItem: Text {
|
||||
text: modelData.charAt(0).toUpperCase() + modelData.slice(1)
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
highlighted: colorRangeComboBox.highlightedIndex === index
|
||||
|
||||
background: Rectangle {
|
||||
color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent"
|
||||
}
|
||||
}
|
||||
|
||||
onActivated: {
|
||||
Settings.settings.colorRange = model[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Show Cursor"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Record mouse cursor in the video"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: cursorSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: cursorThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.showCursor ? cursorSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.showCursor = !Settings.settings.showCursor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 24
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
283
Widgets/SettingsWindow/Tabs/TimeWeather.qml
Normal file
283
Widgets/SettingsWindow/Tabs/TimeWeather.qml
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.Settings
|
||||
import qs.Components
|
||||
import qs.Widgets.SettingsWindow.Tabs.Components
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
anchors.margins: 0
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 0
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Time"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Use 12 Hour Clock"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: use12HourClockSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: use12HourClockThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.use12HourClock = !Settings.settings.use12HourClock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "US Style Date"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: reverseDayMonthSwitch
|
||||
width: 52
|
||||
height: 32
|
||||
radius: 16
|
||||
color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant
|
||||
border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 2
|
||||
|
||||
Rectangle {
|
||||
id: reverseDayMonthThumb
|
||||
width: 28
|
||||
height: 28
|
||||
radius: 14
|
||||
color: Theme.surface
|
||||
border.color: Theme.outline
|
||||
border.width: 1
|
||||
y: 2
|
||||
x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2
|
||||
|
||||
Behavior on x {
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 16
|
||||
|
||||
Text {
|
||||
text: "Weather"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
Layout.bottomMargin: 8
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "City"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Your city name for weather information"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 40
|
||||
radius: 16
|
||||
color: Theme.surfaceVariant
|
||||
border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline
|
||||
border.width: 1
|
||||
|
||||
TextInput {
|
||||
id: cityInput
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 12
|
||||
anchors.rightMargin: 12
|
||||
anchors.topMargin: 6
|
||||
anchors.bottomMargin: 6
|
||||
text: Settings.settings.weatherCity
|
||||
font.pixelSize: 13
|
||||
color: Theme.textPrimary
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
clip: true
|
||||
focus: true
|
||||
selectByMouse: true
|
||||
activeFocusOnTab: true
|
||||
inputMethodHints: Qt.ImhNone
|
||||
|
||||
onTextChanged: {
|
||||
Settings.settings.weatherCity = text;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.IBeamCursor
|
||||
onClicked: {
|
||||
cityInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 8
|
||||
|
||||
RowLayout {
|
||||
spacing: 8
|
||||
Layout.fillWidth: true
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 4
|
||||
Layout.fillWidth: true
|
||||
|
||||
Text {
|
||||
text: "Temperature Unit"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
color: Theme.textPrimary
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Choose between Celsius and Fahrenheit"
|
||||
font.pixelSize: 12
|
||||
color: Theme.textSecondary
|
||||
wrapMode: Text.WordWrap
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
UnitSelector {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue