1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-04-04 12:52:06 -04:00

network: big feature enrichment

- Dedicated view in settings
- VPN profile management
- Ethernet disconnection
- Turn prompts into floating windows
This commit is contained in:
bbedward
2025-11-29 10:00:05 -05:00
parent 9c887fbe63
commit 1d3fe81ff7
51 changed files with 9807 additions and 2500 deletions

View File

@@ -1,71 +1,62 @@
import QtQuick
import Quickshell.Hyprland
import Quickshell
import qs.Common
import qs.Modals.Common
import qs.Services
import qs.Widgets
DankModal {
FloatingWindow {
id: root
layerNamespace: "dms:polkit"
HyprlandFocusGrab {
windows: [root.contentWindow]
active: CompositorService.isHyprland && root.shouldHaveFocus
}
property string passwordInput: ""
property var currentFlow: PolkitService.agent?.flow
property bool isLoading: false
property real minHeight: 240
property int calculatedHeight: Math.max(240, headerRow.implicitHeight + mainColumn.implicitHeight + Theme.spacingM * 3)
function focusPasswordField() {
passwordField.forceActiveFocus();
}
function show() {
passwordInput = "";
isLoading = false;
open();
Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus();
}
});
visible = true;
Qt.callLater(focusPasswordField);
}
shouldBeVisible: false
modalWidth: 420
modalHeight: Math.max(minHeight, contentLoader.item ? contentLoader.item.implicitHeight + Theme.spacingM * 2 : 240)
function hide() {
visible = false;
}
Connections {
target: contentLoader.item
function onImplicitHeightChanged() {
if (shouldBeVisible && contentLoader.item) {
const newHeight = contentLoader.item.implicitHeight + Theme.spacingM * 2;
if (newHeight > minHeight) {
minHeight = newHeight;
}
}
function submitAuth() {
if (passwordInput.length === 0 || !currentFlow || isLoading)
return;
isLoading = true;
currentFlow.submit(passwordInput);
passwordInput = "";
}
function cancelAuth() {
if (!currentFlow || isLoading)
return;
currentFlow.cancelAuthenticationRequest();
}
objectName: "polkitAuthModal"
title: I18n.tr("Authentication")
minimumSize: Qt.size(420, calculatedHeight)
maximumSize: Qt.size(420, calculatedHeight)
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
visible: false
onVisibleChanged: {
if (visible) {
Qt.callLater(focusPasswordField);
return;
}
}
onOpened: {
Qt.callLater(() => {
if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus();
}
});
}
onDialogClosed: {
passwordInput = "";
isLoading = false;
}
onBackgroundClicked: () => {
if (currentFlow && !isLoading) {
currentFlow.cancelAuthenticationRequest();
}
}
Connections {
target: PolkitService.agent
enabled: PolkitService.polkitAvailable
@@ -75,9 +66,8 @@ DankModal {
}
function onIsActiveChanged() {
if (!(PolkitService.agent?.isActive ?? false)) {
close();
}
if (!(PolkitService.agent?.isActive ?? false))
hide();
}
}
@@ -86,17 +76,15 @@ DankModal {
enabled: currentFlow !== null
function onIsResponseRequiredChanged() {
if (currentFlow.isResponseRequired) {
isLoading = false;
passwordInput = "";
if (contentLoader.item && contentLoader.item.passwordField) {
contentLoader.item.passwordField.forceActiveFocus();
}
}
if (!currentFlow.isResponseRequired)
return;
isLoading = false;
passwordInput = "";
passwordField.forceActiveFocus();
}
function onAuthenticationSucceeded() {
close();
hide();
}
function onAuthenticationFailed() {
@@ -104,166 +92,138 @@ DankModal {
}
function onAuthenticationRequestCancelled() {
close();
hide();
}
}
content: Component {
FocusScope {
id: authContent
FocusScope {
id: contentFocusScope
property alias passwordField: passwordField
anchors.fill: parent
focus: true
anchors.fill: parent
focus: true
implicitHeight: headerRow.implicitHeight + mainColumn.implicitHeight + Theme.spacingM
Keys.onEscapePressed: event => {
cancelAuth();
event.accepted = true;
}
Keys.onEscapePressed: event => {
if (currentFlow && !isLoading) {
currentFlow.cancelAuthenticationRequest();
Row {
id: headerRow
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.topMargin: Theme.spacingM
Column {
width: parent.width - 40
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Authentication Required")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
event.accepted = true;
}
Row {
id: headerRow
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.topMargin: Theme.spacingM
Column {
width: parent.width - 40
width: parent.width
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Authentication Required")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Medium
}
Column {
text: currentFlow?.message ?? ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
spacing: Theme.spacingXS
StyledText {
text: currentFlow?.message ?? ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
wrapMode: Text.Wrap
}
StyledText {
visible: (currentFlow?.supplementaryMessage ?? "") !== ""
text: currentFlow?.supplementaryMessage ?? ""
font.pixelSize: Theme.fontSizeSmall
color: (currentFlow?.supplementaryIsError ?? false) ? Theme.error : Theme.surfaceTextMedium
width: parent.width
wrapMode: Text.Wrap
opacity: (currentFlow?.supplementaryIsError ?? false) ? 1 : 0.8
}
wrapMode: Text.Wrap
}
}
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
enabled: !isLoading
opacity: enabled ? 1 : 0.5
onClicked: () => {
if (currentFlow) {
currentFlow.cancelAuthenticationRequest();
}
StyledText {
visible: (currentFlow?.supplementaryMessage ?? "") !== ""
text: currentFlow?.supplementaryMessage ?? ""
font.pixelSize: Theme.fontSizeSmall
color: (currentFlow?.supplementaryIsError ?? false) ? Theme.error : Theme.surfaceTextMedium
width: parent.width
wrapMode: Text.Wrap
opacity: (currentFlow?.supplementaryIsError ?? false) ? 1 : 0.8
}
}
}
Column {
id: mainColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.bottomMargin: Theme.spacingM
spacing: Theme.spacingM
DankActionButton {
iconName: "close"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceText
enabled: !isLoading
opacity: enabled ? 1 : 0.5
onClicked: cancelAuth()
}
}
Column {
id: mainColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.leftMargin: Theme.spacingM
anchors.rightMargin: Theme.spacingM
anchors.bottomMargin: Theme.spacingM
spacing: Theme.spacingM
StyledText {
text: currentFlow?.inputPrompt ?? ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
visible: (currentFlow?.inputPrompt ?? "") !== ""
}
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: Theme.surfaceHover
border.color: passwordField.activeFocus ? Theme.primary : Theme.outlineStrong
border.width: passwordField.activeFocus ? 2 : 1
opacity: isLoading ? 0.5 : 1
MouseArea {
anchors.fill: parent
enabled: !isLoading
onClicked: passwordField.forceActiveFocus()
}
DankTextField {
id: passwordField
anchors.fill: parent
font.pixelSize: Theme.fontSizeMedium
textColor: Theme.surfaceText
text: passwordInput
echoMode: (currentFlow?.responseVisible ?? false) ? TextInput.Normal : TextInput.Password
placeholderText: ""
backgroundColor: "transparent"
enabled: !isLoading
onTextEdited: passwordInput = text
onAccepted: submitAuth()
}
}
Item {
width: parent.width
height: (currentFlow?.failed ?? false) ? failedText.implicitHeight : 0
visible: height > 0
StyledText {
text: currentFlow?.inputPrompt ?? ""
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
id: failedText
text: I18n.tr("Authentication failed, please try again")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
width: parent.width
visible: (currentFlow?.inputPrompt ?? "") !== ""
}
opacity: (currentFlow?.failed ?? false) ? 1 : 0
Rectangle {
width: parent.width
height: 50
radius: Theme.cornerRadius
color: Theme.surfaceHover
border.color: passwordField.activeFocus ? Theme.primary : Theme.outlineStrong
border.width: passwordField.activeFocus ? 2 : 1
opacity: isLoading ? 0.5 : 1
MouseArea {
anchors.fill: parent
enabled: !isLoading
onClicked: () => {
passwordField.forceActiveFocus();
}
}
DankTextField {
id: passwordField
anchors.fill: parent
font.pixelSize: Theme.fontSizeMedium
textColor: Theme.surfaceText
text: passwordInput
echoMode: (currentFlow?.responseVisible ?? false) ? TextInput.Normal : TextInput.Password
placeholderText: ""
backgroundColor: "transparent"
enabled: !isLoading
onTextEdited: () => {
passwordInput = text;
}
onAccepted: () => {
if (passwordInput.length > 0 && currentFlow && !isLoading) {
isLoading = true;
currentFlow.submit(passwordInput);
passwordInput = "";
}
}
}
}
Item {
width: parent.width
height: (currentFlow?.failed ?? false) ? failedText.implicitHeight : 0
visible: height > 0
StyledText {
id: failedText
text: I18n.tr("Authentication failed, please try again")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
width: parent.width
opacity: (currentFlow?.failed ?? false) ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
Behavior on height {
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
@@ -271,89 +231,82 @@ DankModal {
}
}
Item {
width: parent.width
height: 40
Behavior on height {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Item {
width: parent.width
height: 40
Rectangle {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
enabled: !isLoading
opacity: enabled ? 1 : 0.5
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
StyledText {
id: cancelText
Rectangle {
width: Math.max(70, cancelText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: cancelArea.containsMouse ? Theme.surfaceTextHover : "transparent"
border.color: Theme.surfaceVariantAlpha
border.width: 1
enabled: !isLoading
opacity: enabled ? 1 : 0.5
anchors.centerIn: parent
text: I18n.tr("Cancel")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
MouseArea {
id: cancelArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: () => {
if (currentFlow) {
currentFlow.cancelAuthenticationRequest();
}
}
}
StyledText {
id: cancelText
anchors.centerIn: parent
text: I18n.tr("Cancel")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: Font.Medium
}
Rectangle {
width: Math.max(80, authText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: authArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: !isLoading && (passwordInput.length > 0 || !(currentFlow?.isResponseRequired ?? true))
opacity: enabled ? 1 : 0.5
MouseArea {
id: cancelArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: cancelAuth()
}
}
StyledText {
id: authText
Rectangle {
width: Math.max(80, authText.contentWidth + Theme.spacingM * 2)
height: 36
radius: Theme.cornerRadius
color: authArea.containsMouse ? Qt.darker(Theme.primary, 1.1) : Theme.primary
enabled: !isLoading && (passwordInput.length > 0 || !(currentFlow?.isResponseRequired ?? true))
opacity: enabled ? 1 : 0.5
anchors.centerIn: parent
text: I18n.tr("Authenticate")
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
StyledText {
id: authText
anchors.centerIn: parent
text: I18n.tr("Authenticate")
font.pixelSize: Theme.fontSizeMedium
color: Theme.background
font.weight: Font.Medium
}
MouseArea {
id: authArea
MouseArea {
id: authArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: submitAuth()
}
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
enabled: parent.enabled
onClicked: () => {
if (currentFlow && !isLoading) {
isLoading = true;
currentFlow.submit(passwordInput);
passwordInput = "";
}
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -129,13 +129,30 @@ FocusScope {
}
Loader {
id: launcherLoader
id: networkLoader
anchors.fill: parent
active: root.currentIndex === 6
visible: active
focus: active
sourceComponent: NetworkTab {}
onActiveChanged: {
if (active && item) {
Qt.callLater(() => item.forceActiveFocus());
}
}
}
Loader {
id: launcherLoader
anchors.fill: parent
active: root.currentIndex === 7
visible: active
focus: active
sourceComponent: LauncherTab {}
onActiveChanged: {
@@ -149,7 +166,7 @@ FocusScope {
id: themeColorsLoader
anchors.fill: parent
active: root.currentIndex === 7
active: root.currentIndex === 8
visible: active
focus: active
@@ -166,7 +183,7 @@ FocusScope {
id: powerLoader
anchors.fill: parent
active: root.currentIndex === 8
active: root.currentIndex === 9
visible: active
focus: active
@@ -183,7 +200,7 @@ FocusScope {
id: pluginsLoader
anchors.fill: parent
active: root.currentIndex === 9
active: root.currentIndex === 10
visible: active
focus: active
@@ -202,7 +219,7 @@ FocusScope {
id: aboutLoader
anchors.fill: parent
active: root.currentIndex === 10
active: root.currentIndex === 11
visible: active
focus: active

View File

@@ -112,7 +112,7 @@ FloatingWindow {
focus: true
Keys.onPressed: event => {
const tabCount = 11;
const tabCount = 12;
if (event.key === Qt.Key_Escape) {
hide();
event.accepted = true;

View File

@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Modals.Settings
import qs.Services
import qs.Widgets
Rectangle {
@@ -10,59 +11,81 @@ Rectangle {
property int currentIndex: 0
property var parentModal: null
readonly property var sidebarItems: [
readonly property var allSidebarItems: [
{
"text": I18n.tr("Personalization"),
"icon": "person"
"icon": "person",
"tabIndex": 0
},
{
"text": I18n.tr("Time & Weather"),
"icon": "schedule"
"icon": "schedule",
"tabIndex": 1
},
{
"text": I18n.tr("Dank Bar"),
"icon": "toolbar"
"icon": "toolbar",
"tabIndex": 2
},
{
"text": I18n.tr("Widgets"),
"icon": "widgets"
"icon": "widgets",
"tabIndex": 3
},
{
"text": I18n.tr("Dock"),
"icon": "dock_to_bottom"
"icon": "dock_to_bottom",
"tabIndex": 4
},
{
"text": I18n.tr("Displays"),
"icon": "monitor"
"icon": "monitor",
"tabIndex": 5
},
{
"text": I18n.tr("Network"),
"icon": "wifi",
"dmsOnly": true,
"tabIndex": 6
},
{
"text": I18n.tr("Launcher"),
"icon": "apps"
"icon": "apps",
"tabIndex": 7
},
{
"text": I18n.tr("Theme & Colors"),
"icon": "palette"
"icon": "palette",
"tabIndex": 8
},
{
"text": I18n.tr("Power & Security"),
"icon": "power"
"icon": "power",
"tabIndex": 9
},
{
"text": I18n.tr("Plugins"),
"icon": "extension"
"icon": "extension",
"tabIndex": 10
},
{
"text": I18n.tr("About"),
"icon": "info"
"icon": "info",
"tabIndex": 11
}
]
readonly property var sidebarItems: allSidebarItems.filter(item => !item.dmsOnly || !NetworkService.usingLegacy)
function navigateNext() {
currentIndex = (currentIndex + 1) % sidebarItems.length;
const currentItemIndex = sidebarItems.findIndex(item => item.tabIndex === currentIndex);
const nextIndex = (currentItemIndex + 1) % sidebarItems.length;
currentIndex = sidebarItems[nextIndex].tabIndex;
}
function navigatePrevious() {
currentIndex = (currentIndex - 1 + sidebarItems.length) % sidebarItems.length;
const currentItemIndex = sidebarItems.findIndex(item => item.tabIndex === currentIndex);
const prevIndex = (currentItemIndex - 1 + sidebarItems.length) % sidebarItems.length;
currentIndex = sidebarItems[prevIndex].tabIndex;
}
width: 270
@@ -111,7 +134,7 @@ Rectangle {
required property int index
required property var modelData
property bool isActive: sidebarContainer.currentIndex === index
property bool isActive: sidebarContainer.currentIndex === modelData.tabIndex
width: parent.width - parent.leftPadding - parent.rightPadding
height: 44
@@ -147,7 +170,7 @@ Rectangle {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: () => {
sidebarContainer.currentIndex = index;
sidebarContainer.currentIndex = modelData.tabIndex;
}
}

File diff suppressed because it is too large Load Diff