1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-13 00:42:49 -05:00

switch hto monorepo structure

This commit is contained in:
bbedward
2025-11-12 17:18:45 -05:00
parent 6013c994a6
commit 24e800501a
768 changed files with 76284 additions and 221 deletions

View File

@@ -0,0 +1,120 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
property string iconName: ""
property string text: ""
property string secondaryText: ""
property bool isActive: false
property bool enabled: true
property int widgetIndex: 0
property var widgetData: null
property bool editMode: false
signal clicked()
width: parent ? parent.width : 200
height: 60
radius: {
if (Theme.cornerRadius === 0) return 0
return isActive ? Theme.cornerRadius : Theme.cornerRadius + 4
}
readonly property color _tileBgActive: Theme.primary
readonly property color _tileBgInactive:
Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
readonly property color _tileRingActive:
Qt.rgba(Theme.primaryText.r, Theme.primaryText.g, Theme.primaryText.b, 0.22)
color: isActive ? _tileBgActive : _tileBgInactive
border.color: isActive ? _tileRingActive : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: isActive ? 1 : 1
opacity: enabled ? 1.0 : 0.6
function hoverTint(base) {
const factor = 1.2
return Theme.isLightMode ? Qt.darker(base, factor) : Qt.lighter(base, factor)
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? hoverTint(root.color) : "transparent"
opacity: mouseArea.containsMouse ? 0.08 : 0.0
Behavior on opacity {
NumberAnimation { duration: Theme.shortDuration }
}
}
Row {
anchors.fill: parent
anchors.leftMargin: Theme.spacingL + 2
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingM
DankIcon {
name: root.iconName
size: Theme.iconSize
color: isActive ? Theme.primaryText : Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Item {
width: parent.width - Theme.iconSize - parent.spacing
height: parent.height
Column {
anchors.left: parent.left
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Typography {
width: parent.width
text: root.text
style: Typography.Style.Body
color: isActive ? Theme.primaryText : Theme.surfaceText
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
Typography {
width: parent.width
text: root.secondaryText
style: Typography.Style.Caption
color: isActive ? Theme.primaryText : Theme.surfaceVariantText
visible: text.length > 0
elide: Text.ElideRight
wrapMode: Text.NoWrap
}
}
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: root.enabled
onClicked: root.clicked()
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
Behavior on radius {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -0,0 +1,220 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Modules.ControlCenter.Details
import qs.Modules.ControlCenter.Models
Item {
id: root
property string expandedSection: ""
property var expandedWidgetData: null
property var bluetoothCodecSelector: null
property string screenName: ""
property var pluginDetailInstance: null
property var widgetModel: null
property var collapseCallback: null
Loader {
id: pluginDetailLoader
width: parent.width
height: 250
y: Theme.spacingS
active: false
sourceComponent: null
}
Loader {
id: coreDetailLoader
width: parent.width
height: 250
y: Theme.spacingS
active: false
sourceComponent: null
}
Connections {
target: coreDetailLoader.item
enabled: root.expandedSection.startsWith("brightnessSlider_")
ignoreUnknownSignals: true
function onDeviceNameChanged(newDeviceName) {
if (root.expandedWidgetData && root.expandedWidgetData.id === "brightnessSlider") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "brightnessSlider" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.deviceName = newDeviceName
return updatedWidget
}
return w
})
SettingsData.set("controlCenterWidgets", newWidgets)
if (root.collapseCallback) {
root.collapseCallback()
}
}
}
}
Connections {
target: coreDetailLoader.item
enabled: root.expandedSection.startsWith("diskUsage_")
ignoreUnknownSignals: true
function onMountPathChanged(newMountPath) {
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.mountPath = newMountPath
return updatedWidget
}
return w
})
SettingsData.set("controlCenterWidgets", newWidgets)
if (root.collapseCallback) {
root.collapseCallback()
}
}
}
}
onExpandedSectionChanged: {
if (pluginDetailInstance) {
pluginDetailInstance.destroy()
pluginDetailInstance = null
}
pluginDetailLoader.active = false
coreDetailLoader.active = false
if (!root.expandedSection) {
return
}
if (root.expandedSection.startsWith("builtin_")) {
const builtinId = root.expandedSection
let builtinInstance = null
if (builtinId === "builtin_vpn") {
if (widgetModel?.vpnLoader) {
widgetModel.vpnLoader.active = true
}
builtinInstance = widgetModel.vpnBuiltinInstance
}
if (builtinId === "builtin_cups") {
if (widgetModel?.cupsLoader) {
widgetModel.cupsLoader.active = true
}
builtinInstance = widgetModel.cupsBuiltinInstance
}
if (!builtinInstance || !builtinInstance.ccDetailContent) {
return
}
pluginDetailLoader.sourceComponent = builtinInstance.ccDetailContent
pluginDetailLoader.active = parent.height > 0
return
}
if (root.expandedSection.startsWith("plugin_")) {
const pluginId = root.expandedSection.replace("plugin_", "")
const pluginComponent = PluginService.pluginWidgetComponents[pluginId]
if (!pluginComponent) {
return
}
pluginDetailInstance = pluginComponent.createObject(null)
if (!pluginDetailInstance || !pluginDetailInstance.ccDetailContent) {
if (pluginDetailInstance) {
pluginDetailInstance.destroy()
pluginDetailInstance = null
}
return
}
pluginDetailLoader.sourceComponent = pluginDetailInstance.ccDetailContent
pluginDetailLoader.active = parent.height > 0
return
}
if (root.expandedSection.startsWith("diskUsage_")) {
coreDetailLoader.sourceComponent = diskUsageDetailComponent
coreDetailLoader.active = parent.height > 0
return
}
if (root.expandedSection.startsWith("brightnessSlider_")) {
coreDetailLoader.sourceComponent = brightnessDetailComponent
coreDetailLoader.active = parent.height > 0
return
}
switch (root.expandedSection) {
case "network":
case "wifi": coreDetailLoader.sourceComponent = networkDetailComponent; break
case "bluetooth": coreDetailLoader.sourceComponent = bluetoothDetailComponent; break
case "audioOutput": coreDetailLoader.sourceComponent = audioOutputDetailComponent; break
case "audioInput": coreDetailLoader.sourceComponent = audioInputDetailComponent; break
case "battery": coreDetailLoader.sourceComponent = batteryDetailComponent; break
default: return
}
coreDetailLoader.active = parent.height > 0
}
Component {
id: networkDetailComponent
NetworkDetail {}
}
Component {
id: bluetoothDetailComponent
BluetoothDetail {
id: bluetoothDetail
onShowCodecSelector: function(device) {
if (root.bluetoothCodecSelector) {
root.bluetoothCodecSelector.show(device)
root.bluetoothCodecSelector.codecSelected.connect(function(deviceAddress, codecName) {
bluetoothDetail.updateDeviceCodecDisplay(deviceAddress, codecName)
})
}
}
}
}
Component {
id: audioOutputDetailComponent
AudioOutputDetail {}
}
Component {
id: audioInputDetailComponent
AudioInputDetail {}
}
Component {
id: batteryDetailComponent
BatteryDetail {}
}
Component {
id: diskUsageDetailComponent
DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || ""
}
}
Component {
id: brightnessDetailComponent
BrightnessDetail {
initialDeviceName: root.expandedWidgetData?.deviceName || ""
instanceId: root.expandedWidgetData?.instanceId || ""
screenName: root.screenName
}
}
}

View File

@@ -0,0 +1,91 @@
import QtQuick
import qs.Common
import qs.Modules.ControlCenter.Details
Item {
id: root
property string expandedSection: ""
property var expandedWidgetData: null
height: active ? 250 : 0
visible: active
readonly property bool active: expandedSection !== ""
Behavior on height {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Easing.OutCubic
}
}
Loader {
anchors.fill: parent
anchors.topMargin: Theme.spacingS
sourceComponent: {
if (!root.active) return null
if (expandedSection.startsWith("diskUsage_")) {
return diskUsageDetailComponent
}
switch (expandedSection) {
case "wifi": return networkDetailComponent
case "bluetooth": return bluetoothDetailComponent
case "audioOutput": return audioOutputDetailComponent
case "audioInput": return audioInputDetailComponent
case "battery": return batteryDetailComponent
default: return null
}
}
}
Component {
id: networkDetailComponent
NetworkDetail {}
}
Component {
id: bluetoothDetailComponent
BluetoothDetail {}
}
Component {
id: audioOutputDetailComponent
AudioOutputDetail {}
}
Component {
id: audioInputDetailComponent
AudioInputDetail {}
}
Component {
id: batteryDetailComponent
BatteryDetail {}
}
Component {
id: diskUsageDetailComponent
DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || ""
onMountPathChanged: (newMountPath) => {
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.mountPath = newMountPath
return updatedWidget
}
return w
})
SettingsData.set("controlCenterWidgets", newWidgets)
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,289 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
property bool editMode: false
property var widgetData: null
property int widgetIndex: -1
property bool isSlider: false
property Component widgetComponent: null
property real gridCellWidth: 100
property real gridCellHeight: 60
property int gridColumns: 4
property var gridLayout: null
z: dragArea.drag.active ? 10000 : 1
signal widgetMoved(int fromIndex, int toIndex)
signal removeWidget(int index)
signal toggleWidgetSize(int index)
width: {
const widgetWidth = widgetData?.width || 50
if (widgetWidth <= 25) return gridCellWidth
else if (widgetWidth <= 50) return gridCellWidth * 2
else if (widgetWidth <= 75) return gridCellWidth * 3
else return gridCellWidth * 4
}
height: isSlider ? 16 : gridCellHeight
Rectangle {
id: dragIndicator
anchors.fill: parent
color: "transparent"
border.color: Theme.primary
border.width: dragArea.drag.active ? 2 : 0
radius: Theme.cornerRadius
opacity: dragArea.drag.active ? 0.8 : 1.0
z: dragArea.drag.active ? 10000 : 1
Behavior on border.width {
NumberAnimation { duration: 150 }
}
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
Loader {
id: widgetLoader
anchors.fill: parent
sourceComponent: widgetComponent
property var widgetData: root.widgetData
property int widgetIndex: root.widgetIndex
property int globalWidgetIndex: root.widgetIndex
property int widgetWidth: root.widgetData?.width || 50
MouseArea {
id: editModeBlocker
anchors.fill: parent
enabled: root.editMode
acceptedButtons: Qt.AllButtons
onPressed: function(mouse) { mouse.accepted = true }
onWheel: function(wheel) { wheel.accepted = true }
z: 100
}
}
MouseArea {
id: dragArea
anchors.fill: parent
enabled: editMode
cursorShape: editMode ? Qt.OpenHandCursor : Qt.PointingHandCursor
drag.target: editMode ? root : null
drag.axis: Drag.XAndYAxis
drag.smoothed: true
onPressed: function(mouse) {
if (editMode) {
cursorShape = Qt.ClosedHandCursor
if (root.gridLayout && root.gridLayout.moveToTop) {
root.gridLayout.moveToTop(root)
}
}
}
onReleased: function(mouse) {
if (editMode) {
cursorShape = Qt.OpenHandCursor
root.snapToGrid()
}
}
}
Drag.active: dragArea.drag.active
Drag.hotSpot.x: width / 2
Drag.hotSpot.y: height / 2
function swapIndices(i, j) {
if (i === j) return;
const arr = SettingsData.controlCenterWidgets;
if (!arr || i < 0 || j < 0 || i >= arr.length || j >= arr.length) return;
const copy = arr.slice();
const tmp = copy[i];
copy[i] = copy[j];
copy[j] = tmp;
SettingsData.set("controlCenterWidgets", copy);
}
function snapToGrid() {
if (!editMode || !gridLayout) return
const globalPos = root.mapToItem(gridLayout, 0, 0)
const cellWidth = gridLayout.width / gridColumns
const cellHeight = gridCellHeight + Theme.spacingS
const centerX = globalPos.x + (root.width / 2)
const centerY = globalPos.y + (root.height / 2)
let targetCol = Math.max(0, Math.floor(centerX / cellWidth))
let targetRow = Math.max(0, Math.floor(centerY / cellHeight))
targetCol = Math.min(targetCol, gridColumns - 1)
const newIndex = findBestInsertionIndex(targetRow, targetCol)
if (newIndex !== widgetIndex && newIndex >= 0 && newIndex < (SettingsData.controlCenterWidgets?.length || 0)) {
swapIndices(widgetIndex, newIndex)
}
}
function findBestInsertionIndex(targetRow, targetCol) {
const widgets = SettingsData.controlCenterWidgets || [];
const n = widgets.length;
if (!n || widgetIndex < 0 || widgetIndex >= n) return -1;
function spanFor(width) {
const w = width ?? 50;
if (w <= 25) return 1;
if (w <= 50) return 2;
if (w <= 75) return 3;
return 4;
}
const cols = gridColumns || 4;
let row = 0, col = 0;
let draggedOrigKey = null;
const pos = [];
for (let i = 0; i < n; i++) {
const span = Math.min(spanFor(widgets[i].width), cols);
if (col + span > cols) {
row++;
col = 0;
}
const startCol = col;
const centerKey = row * cols + (startCol + (span - 1) / 2);
if (i === widgetIndex) {
draggedOrigKey = centerKey;
} else {
pos.push({ index: i, row, startCol, span, centerKey });
}
col += span;
if (col >= cols) {
row++;
col = 0;
}
}
if (pos.length === 0) return -1;
const centerColCoord = targetCol + 0.5;
const targetKey = targetRow * cols + centerColCoord;
for (let k = 0; k < pos.length; k++) {
const p = pos[k];
if (p.row === targetRow && centerColCoord >= p.startCol && centerColCoord < (p.startCol + p.span)) {
return p.index;
}
}
let lo = 0, hi = pos.length - 1;
if (targetKey <= pos[0].centerKey) return pos[0].index;
if (targetKey >= pos[hi].centerKey) return pos[hi].index;
while (lo <= hi) {
const mid = (lo + hi) >> 1;
const mk = pos[mid].centerKey;
if (targetKey < mk) hi = mid - 1;
else if (targetKey > mk) lo = mid + 1;
else return pos[mid].index;
}
const movingUp = (draggedOrigKey != null) ? (targetKey < draggedOrigKey) : false;
return (movingUp ? pos[lo].index : pos[hi].index);
}
Rectangle {
width: 16
height: 16
radius: 8
color: Theme.error
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: -4
visible: editMode
z: 10
DankIcon {
anchors.centerIn: parent
name: "close"
size: 12
color: Theme.primaryText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: removeWidget(widgetIndex)
}
}
SizeControls {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: -6
visible: editMode
z: 10
currentSize: root.widgetData?.width || 50
isSlider: root.isSlider
widgetIndex: root.widgetIndex
onSizeChanged: (newSize) => {
var widgets = SettingsData.controlCenterWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
widgets[widgetIndex].width = newSize
SettingsData.set("controlCenterWidgets", widgets)
}
}
}
Rectangle {
id: dragHandle
width: 16
height: 12
radius: 2
color: Theme.primary
anchors.top: parent.top
anchors.left: parent.left
anchors.margins: 4
visible: editMode
z: 15
opacity: dragArea.drag.active ? 1.0 : 0.7
DankIcon {
anchors.centerIn: parent
name: "drag_indicator"
size: 10
color: Theme.primaryText
}
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
Rectangle {
anchors.fill: parent
color: editMode ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : "transparent"
radius: Theme.cornerRadius
border.color: "transparent"
border.width: 0
z: -1
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
}
}

View File

@@ -0,0 +1,240 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Widgets
Row {
id: root
property var availableWidgets: []
property Item popoutContent: null
signal addWidget(string widgetId)
signal resetToDefault()
signal clearAll()
height: 48
spacing: Theme.spacingS
onAddWidget: addWidgetPopup.close()
Popup {
id: addWidgetPopup
parent: popoutContent
x: parent ? Math.round((parent.width - width) / 2) : 0
y: parent ? Math.round((parent.height - height) / 2) : 0
width: 400
height: 300
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
background: Rectangle {
color: Theme.surfaceContainer
border.color: Theme.primarySelected
border.width: 0
radius: Theme.cornerRadius
}
contentItem: Item {
anchors.fill: parent
anchors.margins: Theme.spacingL
Row {
id: headerRow
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: Theme.spacingM
DankIcon {
name: "add_circle"
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: I18n.tr("Add Widget")
style: Typography.Style.Subtitle
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
DankListView {
anchors.top: headerRow.bottom
anchors.topMargin: Theme.spacingM
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
spacing: Theme.spacingS
clip: true
model: root.availableWidgets
delegate: Rectangle {
width: 400 - Theme.spacingL * 2
height: 50
radius: Theme.cornerRadius
color: widgetMouseArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
border.width: 0
Row {
anchors.fill: parent
anchors.margins: Theme.spacingM
spacing: Theme.spacingM
DankIcon {
name: modelData.icon
size: Theme.iconSize
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
width: 400 - Theme.spacingL * 2 - Theme.iconSize - Theme.spacingM * 3 - Theme.iconSize
Typography {
text: modelData.text
style: Typography.Style.Body
color: Theme.surfaceText
elide: Text.ElideRight
width: parent.width
}
Typography {
text: modelData.description
style: Typography.Style.Caption
color: Theme.outline
elide: Text.ElideRight
width: parent.width
}
}
DankIcon {
name: "add"
size: Theme.iconSize - 4
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: widgetMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.addWidget(modelData.id)
}
}
}
}
}
}
Rectangle {
width: (parent.width - Theme.spacingS * 2) / 3
height: 48
radius: Theme.cornerRadius
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
border.color: Theme.primary
border.width: 0
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: "add"
size: Theme.iconSize - 2
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: I18n.tr("Add Widget")
style: Typography.Style.Button
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: addWidgetPopup.open()
}
}
Rectangle {
width: (parent.width - Theme.spacingS * 2) / 3
height: 48
radius: Theme.cornerRadius
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12)
border.color: Theme.warning
border.width: 0
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: "settings_backup_restore"
size: Theme.iconSize - 2
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: I18n.tr("Defaults")
style: Typography.Style.Button
color: Theme.warning
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.resetToDefault()
}
}
Rectangle {
width: (parent.width - Theme.spacingS * 2) / 3
height: 48
radius: Theme.cornerRadius
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12)
border.color: Theme.error
border.width: 0
Row {
anchors.centerIn: parent
spacing: Theme.spacingS
DankIcon {
name: "clear_all"
size: Theme.iconSize - 2
color: Theme.error
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: I18n.tr("Reset")
style: Typography.Style.Button
color: Theme.error
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.clearAll()
}
}
}

View File

@@ -0,0 +1,114 @@
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property bool editMode: false
signal powerButtonClicked()
signal lockRequested()
signal editModeToggled()
signal settingsButtonClicked()
implicitHeight: 70
radius: Theme.cornerRadius
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: 0
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingL
anchors.rightMargin: Theme.spacingL
spacing: Theme.spacingM
DankCircularImage {
id: avatarContainer
width: 60
height: 60
imageSource: {
if (PortalService.profileImage === "")
return ""
if (PortalService.profileImage.startsWith("/"))
return "file://" + PortalService.profileImage
return PortalService.profileImage
}
fallbackIcon: "person"
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Typography {
text: UserInfoService.fullName
|| UserInfoService.username || "User"
style: Typography.Style.Subtitle
color: Theme.surfaceText
}
Typography {
text: (UserInfoService.uptime || "Unknown")
style: Typography.Style.Caption
color: Theme.surfaceVariantText
}
}
}
Row {
id: actionButtonsRow
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.spacingXS
spacing: Theme.spacingXS
DankActionButton {
buttonSize: 36
iconName: "lock"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
backgroundColor: "transparent"
onClicked: {
root.lockRequested()
}
}
DankActionButton {
buttonSize: 36
iconName: "power_settings_new"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
backgroundColor: "transparent"
onClicked: root.powerButtonClicked()
}
DankActionButton {
buttonSize: 36
iconName: "settings"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
backgroundColor: "transparent"
onClicked: {
root.settingsButtonClicked()
settingsModal.show()
}
}
DankActionButton {
buttonSize: 36
iconName: editMode ? "done" : "edit"
iconSize: Theme.iconSize - 4
iconColor: editMode ? Theme.primary : Theme.surfaceText
backgroundColor: "transparent"
onClicked: root.editModeToggled()
}
}
}

View File

@@ -0,0 +1,52 @@
import QtQuick
import qs.Common
import qs.Widgets
Rectangle {
id: root
property string iconName: ""
property string text: ""
signal pressed()
height: 34
radius: Theme.cornerRadius
color: mouseArea.containsMouse ? Qt.rgba(
Theme.primary.r,
Theme.primary.g,
Theme.primary.b,
0.12) : Qt.rgba(
Theme.surfaceVariant.r,
Theme.surfaceVariant.g,
Theme.surfaceVariant.b,
0.5)
Row {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: root.iconName
size: Theme.fontSizeSmall
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
Typography {
text: root.text
style: Typography.Style.Button
color: mouseArea.containsMouse ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onPressed: root.pressed()
}
}

View File

@@ -0,0 +1,52 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Widgets
Row {
id: root
property int currentSize: 50
property bool isSlider: false
property int widgetIndex: -1
signal sizeChanged(int newSize)
readonly property var availableSizes: isSlider ? [50, 100] : [25, 50, 75, 100]
spacing: 2
Repeater {
model: root.availableSizes
Rectangle {
width: 16
height: 16
radius: 3
color: modelData === root.currentSize ? Theme.primary : Theme.surfaceContainer
border.color: modelData === root.currentSize ? Theme.primary : Theme.outline
border.width: 1
StyledText {
anchors.centerIn: parent
text: modelData.toString()
font.pixelSize: 8
font.weight: Font.Medium
color: modelData === root.currentSize ? Theme.primaryText : Theme.surfaceText
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.currentSize = modelData
root.sizeChanged(modelData)
}
}
Behavior on color {
ColorAnimation { duration: Theme.shortDuration }
}
}
}
}

View File

@@ -0,0 +1,46 @@
import QtQuick
import qs.Common
import qs.Widgets
StyledText {
id: root
enum Style {
Title,
Subtitle,
Body,
Caption,
Button
}
property int style: Typography.Style.Body
font.pixelSize: {
switch (style) {
case Typography.Style.Title: return Theme.fontSizeXLarge
case Typography.Style.Subtitle: return Theme.fontSizeLarge
case Typography.Style.Body: return Theme.fontSizeMedium
case Typography.Style.Caption: return Theme.fontSizeSmall
case Typography.Style.Button: return Theme.fontSizeSmall
default: return Theme.fontSizeMedium
}
}
font.weight: {
switch (style) {
case Typography.Style.Title: return Font.Bold
case Typography.Style.Subtitle: return Font.Medium
case Typography.Style.Body: return Font.Normal
case Typography.Style.Caption: return Font.Normal
case Typography.Style.Button: return Font.Medium
default: return Font.Normal
}
}
color: {
switch (style) {
case Typography.Style.Caption: return Theme.surfaceVariantText
default: return Theme.surfaceText
}
}
}