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

feat(DankDash): configurable drag-n-drop tab arrangement & hide options in dms settings

This commit is contained in:
purian23
2026-06-24 23:33:35 -04:00
parent 2fc3b8ee4a
commit 838d21aae8
10 changed files with 783 additions and 55 deletions
+91
View File
@@ -511,6 +511,97 @@ Singleton {
property bool useAutoLocation: false property bool useAutoLocation: false
property bool weatherEnabled: true property bool weatherEnabled: true
readonly property var _dashTabIds: ["overview", "media", "wallpaper", "weather", "settings"]
readonly property var _dashTabsDefault: [
{
"id": "overview",
"enabled": true
},
{
"id": "media",
"enabled": true
},
{
"id": "wallpaper",
"enabled": true
},
{
"id": "weather",
"enabled": true
},
{
"id": "settings",
"enabled": true
}
]
property var dashTabs: _dashTabsDefault
onDashTabsChanged: saveSettings()
function getDashTabs() {
const stored = Array.isArray(dashTabs) ? dashTabs : [];
const result = [];
const seen = {};
for (var i = 0; i < stored.length; i++) {
const id = stored[i] && stored[i].id;
if (_dashTabIds.indexOf(id) < 0 || seen[id])
continue;
seen[id] = true;
result.push({
"id": id,
"enabled": stored[i].enabled !== false
});
}
for (var j = 0; j < _dashTabIds.length; j++) {
if (!seen[_dashTabIds[j]])
result.push({
"id": _dashTabIds[j],
"enabled": true
});
}
return result;
}
function visibleDashTabIds() {
return getDashTabs().filter(t => t.enabled && (t.id !== "weather" || weatherEnabled)).map(t => t.id);
}
function dashTabIndexForId(id) {
const idx = visibleDashTabIds().indexOf(id);
return idx < 0 ? 0 : idx;
}
function setDashTabOrder(ids) {
const current = getDashTabs();
const ordered = [];
for (var i = 0; i < ids.length; i++) {
const existing = current.find(t => t.id === ids[i]);
if (existing)
ordered.push(existing);
}
for (var j = 0; j < current.length; j++) {
if (ids.indexOf(current[j].id) < 0)
ordered.push(current[j]);
}
dashTabs = ordered;
}
function setDashTabEnabled(id, on) {
const current = getDashTabs();
if (!on && id !== "settings" && current.filter(t => t.enabled && t.id !== "settings").length <= 1)
return;
dashTabs = current.map(t => t.id === id ? {
"id": t.id,
"enabled": on
} : t);
}
function resetDashTabs() {
dashTabs = _dashTabsDefault.map(t => ({
"id": t.id,
"enabled": t.enabled
}));
}
property string networkPreference: "auto" property string networkPreference: "auto"
property string iconThemeDark: "System Default" property string iconThemeDark: "System Default"
@@ -248,6 +248,7 @@ var SPEC = {
useAutoLocation: { def: false }, useAutoLocation: { def: false },
weatherEnabled: { def: true }, weatherEnabled: { def: true },
dashTabs: { def: [{ id: "overview", enabled: true }, { id: "media", enabled: true }, { id: "wallpaper", enabled: true }, { id: "weather", enabled: true }, { id: "settings", enabled: true }] },
networkPreference: { def: "auto" }, networkPreference: { def: "auto" },
+4 -4
View File
@@ -269,13 +269,13 @@ Item {
function resolveTabIndex(tab: string): int { function resolveTabIndex(tab: string): int {
switch ((tab || "").toLowerCase()) { switch ((tab || "").toLowerCase()) {
case "media": case "media":
return 1; return SettingsData.dashTabIndexForId("media");
case "wallpaper": case "wallpaper":
return 2; return SettingsData.dashTabIndexForId("wallpaper");
case "weather": case "weather":
return SettingsData.weatherEnabled ? 3 : 0; return SettingsData.dashTabIndexForId("weather");
default: default:
return 0; return SettingsData.dashTabIndexForId("overview");
} }
} }
@@ -701,5 +701,20 @@ FocusScope {
Qt.callLater(() => item.forceActiveFocus()); Qt.callLater(() => item.forceActiveFocus());
} }
} }
Loader {
id: dankDashLoader
anchors.fill: parent
active: root.currentIndex === 43
visible: active
focus: active
sourceComponent: DankDashTab {}
onActiveChanged: {
if (active && item)
Qt.callLater(() => item.forceActiveFocus());
}
}
} }
} }
@@ -155,6 +155,12 @@ Rectangle {
"icon": "dashboard", "icon": "dashboard",
"collapsedByDefault": true, "collapsedByDefault": true,
"children": [ "children": [
{
"id": "dank_dash",
"text": I18n.tr("Dank Dash"),
"icon": "space_dashboard",
"tabIndex": 43
},
{ {
"id": "media_player", "id": "media_player",
"text": I18n.tr("Media Player"), "text": I18n.tr("Media Player"),
+1 -1
View File
@@ -109,7 +109,7 @@ PanelWindow {
} }
function triggerWallpaperBrowser() { function triggerWallpaperBrowser() {
triggerDashTab(2); triggerDashTab(SettingsData.dashTabIndexForId("wallpaper"));
} }
readonly property bool usesOverlayLayer: CompositorService.framePeerSurfacesUseOverlayForScreen(barWindow.screen) || (barConfig?.useOverlayLayer ?? false) readonly property bool usesOverlayLayer: CompositorService.framePeerSurfacesUseOverlayForScreen(barWindow.screen) || (barConfig?.useOverlayLayer ?? false)
+76 -50
View File
@@ -12,6 +12,62 @@ DankPopout {
property var triggerScreen: null property var triggerScreen: null
property int currentTabIndex: 0 property int currentTabIndex: 0
readonly property var __tabPresentation: ({
"overview": {
"icon": "dashboard",
"text": I18n.tr("Overview")
},
"media": {
"icon": "music_note",
"text": I18n.tr("Media")
},
"wallpaper": {
"icon": "wallpaper",
"text": I18n.tr("Wallpapers")
},
"weather": {
"icon": "wb_sunny",
"text": I18n.tr("Weather")
},
"settings": {
"icon": "settings",
"text": I18n.tr("Settings"),
"isAction": true
}
})
readonly property var orderedTabIds: SettingsData.visibleDashTabIds()
readonly property string currentTabId: orderedTabIds.length > 0 ? (orderedTabIds[Math.min(currentTabIndex, orderedTabIds.length - 1)] ?? "overview") : "overview"
function __isActionTab(id) {
return root.__tabPresentation[id]?.isAction === true;
}
function __resolveContentIndex(idx) {
if (orderedTabIds.length === 0)
return 0;
var clamped = Math.max(0, Math.min(idx, orderedTabIds.length - 1));
if (!__isActionTab(orderedTabIds[clamped]))
return clamped;
for (var f = clamped + 1; f < orderedTabIds.length; f++)
if (!__isActionTab(orderedTabIds[f]))
return f;
for (var b = clamped - 1; b >= 0; b--)
if (!__isActionTab(orderedTabIds[b]))
return b;
return clamped;
}
onOrderedTabIdsChanged: {
var resolved = __resolveContentIndex(currentTabIndex);
if (resolved !== currentTabIndex)
currentTabIndex = resolved;
}
onCurrentTabIndexChanged: {
var resolved = __resolveContentIndex(currentTabIndex);
if (resolved !== currentTabIndex)
currentTabIndex = resolved;
}
popupWidth: SettingsData.showWeekNumber ? 736 : 700 popupWidth: SettingsData.showWeekNumber ? 736 : 700
popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500 popupHeight: contentLoader.item ? contentLoader.item.implicitHeight : 500
triggerWidth: 80 triggerWidth: 80
@@ -191,7 +247,7 @@ DankPopout {
Keys.onPressed: function (event) { Keys.onPressed: function (event) {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
if (root.currentTabIndex === 2 && wallpaperLoader.item?.handleKeyEvent && wallpaperLoader.item.handleKeyEvent(event)) { if (root.currentTabId === "wallpaper" && wallpaperLoader.item?.handleKeyEvent && wallpaperLoader.item.handleKeyEvent(event)) {
event.accepted = true; event.accepted = true;
return; return;
} }
@@ -231,21 +287,21 @@ DankPopout {
return; return;
} }
if (root.currentTabIndex === 0 && overviewLoader.item?.handleKeyEvent) { if (root.currentTabId === "overview" && overviewLoader.item?.handleKeyEvent) {
if (overviewLoader.item.handleKeyEvent(event)) { if (overviewLoader.item.handleKeyEvent(event)) {
event.accepted = true; event.accepted = true;
return; return;
} }
} }
if (root.currentTabIndex === 1 && mediaLoader.item?.handleKeyEvent) { if (root.currentTabId === "media" && mediaLoader.item?.handleKeyEvent) {
if (mediaLoader.item.handleKeyEvent(event)) { if (mediaLoader.item.handleKeyEvent(event)) {
event.accepted = true; event.accepted = true;
return; return;
} }
} }
if (root.currentTabIndex === 2 && wallpaperLoader.item?.handleKeyEvent) { if (root.currentTabId === "wallpaper" && wallpaperLoader.item?.handleKeyEvent) {
if (wallpaperLoader.item.handleKeyEvent(event)) { if (wallpaperLoader.item.handleKeyEvent(event)) {
event.accepted = true; event.accepted = true;
return; return;
@@ -281,44 +337,14 @@ DankPopout {
return item; return item;
} }
model: { model: root.orderedTabIds.map(id => root.__tabPresentation[id])
let tabs = [
{
"icon": "dashboard",
"text": I18n.tr("Overview")
},
{
"icon": "music_note",
"text": I18n.tr("Media")
},
{
"icon": "wallpaper",
"text": I18n.tr("Wallpapers")
}
];
if (SettingsData.weatherEnabled) {
tabs.push({
"icon": "wb_sunny",
"text": I18n.tr("Weather")
});
}
tabs.push({
"icon": "settings",
"text": I18n.tr("Settings"),
"isAction": true
});
return tabs;
}
onTabClicked: function (index) { onTabClicked: function (index) {
root.currentTabIndex = index; root.currentTabIndex = index;
} }
onActionTriggered: function (index) { onActionTriggered: function (index) {
let settingsIndex = SettingsData.weatherEnabled ? 4 : 3; if (root.orderedTabIds[index] === "settings") {
if (index === settingsIndex) {
dashVisible = false; dashVisible = false;
PopoutService.focusOrToggleSettings(); PopoutService.focusOrToggleSettings();
} }
@@ -336,25 +362,25 @@ DankPopout {
height: implicitHeight height: implicitHeight
implicitWidth: currentItem && currentItem.implicitWidth > 0 ? currentItem.implicitWidth : (700 - Theme.spacingM * 2) implicitWidth: currentItem && currentItem.implicitWidth > 0 ? currentItem.implicitWidth : (700 - Theme.spacingM * 2)
implicitHeight: { implicitHeight: {
if (root.currentTabIndex === 0) if (root.currentTabId === "overview")
return overviewLoader.item?.implicitHeight ?? 410; return overviewLoader.item?.implicitHeight ?? 410;
if (root.currentTabIndex === 1) if (root.currentTabId === "media")
return mediaLoader.item?.implicitHeight ?? 410; return mediaLoader.item?.implicitHeight ?? 410;
if (root.currentTabIndex === 2) if (root.currentTabId === "wallpaper")
return wallpaperLoader.item?.implicitHeight ?? 410; return wallpaperLoader.item?.implicitHeight ?? 410;
if (SettingsData.weatherEnabled && root.currentTabIndex === 3) if (root.currentTabId === "weather")
return weatherLoader.item?.implicitHeight ?? 410; return weatherLoader.item?.implicitHeight ?? 410;
return 410; return 410;
} }
readonly property var currentItem: { readonly property var currentItem: {
if (root.currentTabIndex === 0) if (root.currentTabId === "overview")
return overviewLoader.item; return overviewLoader.item;
if (root.currentTabIndex === 1) if (root.currentTabId === "media")
return mediaLoader.item; return mediaLoader.item;
if (root.currentTabIndex === 2) if (root.currentTabId === "wallpaper")
return wallpaperLoader.item; return wallpaperLoader.item;
if (root.currentTabIndex === 3) if (root.currentTabId === "weather")
return weatherLoader.item; return weatherLoader.item;
return null; return null;
} }
@@ -362,7 +388,7 @@ DankPopout {
Loader { Loader {
id: overviewLoader id: overviewLoader
anchors.fill: parent anchors.fill: parent
active: root.currentTabIndex === 0 active: root.currentTabId === "overview"
visible: active visible: active
sourceComponent: Component { sourceComponent: Component {
OverviewTab { OverviewTab {
@@ -370,11 +396,11 @@ DankPopout {
onNavFocusRequested: mainContainer.forceActiveFocus() onNavFocusRequested: mainContainer.forceActiveFocus()
onSwitchToWeatherTab: { onSwitchToWeatherTab: {
if (SettingsData.weatherEnabled) { if (SettingsData.weatherEnabled) {
root.currentTabIndex = 3; root.currentTabIndex = SettingsData.dashTabIndexForId("weather");
} }
} }
onSwitchToMediaTab: { onSwitchToMediaTab: {
root.currentTabIndex = 1; root.currentTabIndex = SettingsData.dashTabIndexForId("media");
} }
} }
} }
@@ -383,7 +409,7 @@ DankPopout {
Loader { Loader {
id: mediaLoader id: mediaLoader
anchors.fill: parent anchors.fill: parent
active: root.currentTabIndex === 1 active: root.currentTabId === "media"
visible: active visible: active
sourceComponent: Component { sourceComponent: Component {
MediaPlayerTab { MediaPlayerTab {
@@ -419,7 +445,7 @@ DankPopout {
Loader { Loader {
id: wallpaperLoader id: wallpaperLoader
anchors.fill: parent anchors.fill: parent
active: root.currentTabIndex === 2 active: root.currentTabId === "wallpaper"
visible: active visible: active
sourceComponent: Component { sourceComponent: Component {
WallpaperTab { WallpaperTab {
@@ -435,7 +461,7 @@ DankPopout {
Loader { Loader {
id: weatherLoader id: weatherLoader
anchors.fill: parent anchors.fill: parent
active: SettingsData.weatherEnabled && root.currentTabIndex === 3 active: root.currentTabId === "weather"
visible: active visible: active
sourceComponent: Component { sourceComponent: Component {
WeatherTab {} WeatherTab {}
+577
View File
@@ -0,0 +1,577 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Common
import qs.Services
import qs.Widgets
Item {
id: root
focus: true
property string highlightedId: ""
readonly property var __presentation: ({
"overview": {
"icon": "dashboard",
"text": I18n.tr("Overview"),
"description": I18n.tr("Clock, calendar, system info and profile")
},
"media": {
"icon": "music_note",
"text": I18n.tr("Media"),
"description": I18n.tr("Now playing and media controls")
},
"wallpaper": {
"icon": "wallpaper",
"text": I18n.tr("Wallpapers"),
"description": I18n.tr("Browse and set wallpapers")
},
"weather": {
"icon": "wb_sunny",
"text": I18n.tr("Weather"),
"description": SettingsData.weatherEnabled ? I18n.tr("Forecast and conditions") : I18n.tr("Hidden until weather is enabled")
},
"settings": {
"icon": "settings",
"text": I18n.tr("Settings"),
"description": I18n.tr("Shortcut that opens this settings window")
}
})
// Stable model: the canonical id list never reorders, so the Repeater keeps
// its delegates alive across commits (preserving focus for keyboard reorder)
readonly property var tabIds: SettingsData._dashTabIds
readonly property var tabState: SettingsData.getDashTabs()
readonly property int enabledContentCount: tabState.filter(t => t.enabled && t.id !== "settings").length
function presentationFor(id) {
return __presentation[id] ?? {
"icon": "tab",
"text": id,
"description": ""
};
}
function isEnabled(id) {
const t = tabState.find(t => t.id === id);
return t ? t.enabled : false;
}
readonly property real rowHeight: 70
readonly property real rowSpacing: Theme.spacingM
readonly property real dividerGap: 40
property var enabledOrder: []
property var disabledOrder: []
property string draggingId: ""
property var dragStartOrder: []
readonly property bool hasHidden: disabledOrder.length > 0
readonly property real dividerY: enabledOrder.length * (rowHeight + rowSpacing)
readonly property real totalHeight: {
const base = enabledOrder.length * (rowHeight + rowSpacing);
if (!hasHidden)
return Math.max(0, base - rowSpacing);
return base + dividerGap + disabledOrder.length * (rowHeight + rowSpacing) - rowSpacing;
}
function rebuild() {
const en = [];
const dis = [];
for (var i = 0; i < tabState.length; i++) {
if (tabState[i].enabled)
en.push(tabState[i].id);
else
dis.push(tabState[i].id);
}
enabledOrder = en;
disabledOrder = dis;
}
onTabStateChanged: rebuild()
Component.onCompleted: rebuild()
function slotYForId(id) {
const p = enabledOrder.indexOf(id);
if (p >= 0)
return p * (rowHeight + rowSpacing);
const k = disabledOrder.indexOf(id);
return dividerY + dividerGap + Math.max(0, k) * (rowHeight + rowSpacing);
}
function beginDrag(id) {
draggingId = id;
dragStartOrder = enabledOrder.slice();
}
function updateDragTarget(centerY) {
if (draggingId === "")
return;
var pos = Math.floor(centerY / (rowHeight + rowSpacing));
pos = Math.max(0, Math.min(pos, enabledOrder.length - 1));
const arr = enabledOrder.slice();
const d = arr.indexOf(draggingId);
if (d < 0 || d === pos)
return;
arr.splice(d, 1);
arr.splice(pos, 0, draggingId);
enabledOrder = arr;
}
function commit() {
SettingsData.setDashTabOrder(enabledOrder.concat(disabledOrder));
}
function endDrag() {
if (draggingId === "")
return;
const changed = JSON.stringify(enabledOrder) !== JSON.stringify(dragStartOrder);
draggingId = "";
if (changed)
commit();
}
function moveEnabled(id, delta) {
const pos = enabledOrder.indexOf(id);
const next = pos + delta;
if (pos < 0 || next < 0 || next >= enabledOrder.length)
return;
const arr = enabledOrder.slice();
arr.splice(pos, 1);
arr.splice(next, 0, id);
enabledOrder = arr;
commit();
}
function canHide(id) {
return !isEnabled(id) || id === "settings" || enabledContentCount > 1;
}
// Keyboard nav is handled at the tab root (not per-row activeFocusOnTab)
Keys.onPressed: function (event) {
const order = enabledOrder.concat(disabledOrder);
if (order.length === 0)
return;
const ctrl = (event.modifiers & Qt.ControlModifier) !== 0;
if (event.key === Qt.Key_Up || event.key === Qt.Key_Down) {
const dir = event.key === Qt.Key_Down ? 1 : -1;
if (ctrl) {
if (highlightedId !== "" && isEnabled(highlightedId))
moveEnabled(highlightedId, dir);
} else if (highlightedId === "") {
highlightedId = dir > 0 ? order[0] : order[order.length - 1];
} else {
var idx = order.indexOf(highlightedId);
idx = Math.max(0, Math.min(order.length - 1, idx + dir));
highlightedId = order[idx];
}
event.accepted = true;
} else if ((event.key === Qt.Key_Space || event.key === Qt.Key_Return) && highlightedId !== "") {
if (canHide(highlightedId))
SettingsData.setDashTabEnabled(highlightedId, !isEnabled(highlightedId));
event.accepted = true;
}
}
DankFlickable {
anchors.fill: parent
clip: true
contentHeight: mainColumn.height + Theme.spacingXL
contentWidth: width
Column {
id: mainColumn
topPadding: 4
width: Math.min(550, parent.width - Theme.spacingL * 2)
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingXL
StyledRect {
width: parent.width
height: headerContent.implicitHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.width: 0
Column {
id: headerContent
anchors.fill: parent
anchors.margins: Theme.spacingL
spacing: Theme.spacingM
RowLayout {
id: headerText
width: parent.width
spacing: Theme.spacingM
DankIcon {
name: "space_dashboard"
size: Theme.iconSize
color: Theme.primary
Layout.alignment: Qt.AlignVCenter
}
StyledText {
text: I18n.tr("Dank Dash")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
Layout.alignment: Qt.AlignVCenter
}
Item {
height: 1
Layout.fillWidth: true
}
Rectangle {
id: resetButton
width: resetContentRow.implicitWidth + Theme.spacingM * 2
height: 28
radius: Theme.cornerRadius
color: resetArea.containsMouse ? Theme.surfacePressed : Theme.surfaceVariant
border.width: 0
Layout.alignment: Qt.AlignVCenter
Row {
id: resetContentRow
anchors.centerIn: parent
spacing: Theme.spacingXS
DankIcon {
name: "refresh"
size: 14
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Reset")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: resetArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: SettingsData.resetDashTabs()
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}
}
StyledText {
text: I18n.tr("Drag to reorder or click to hide tabs. Use ↑/↓ to highlight a tab and Ctrl+↑/↓ to move it.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
wrapMode: Text.WordWrap
}
}
}
StyledRect {
width: parent.width
height: root.totalHeight + Theme.spacingL * 2
radius: Theme.cornerRadius
color: Theme.surfaceContainerHigh
border.width: 0
Item {
id: reorderArea
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Theme.spacingL
height: root.totalHeight
Behavior on height {
NumberAnimation {
duration: Theme.expressiveDurations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
}
}
Item {
id: hiddenDivider
width: parent.width
height: root.dividerGap
y: root.dividerY + (root.rowSpacing / 2)
opacity: root.hasHidden ? 1 : 0
visible: opacity > 0.01
Behavior on y {
NumberAnimation {
duration: Theme.expressiveDurations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveDefaultSpatial
}
}
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
}
}
Row {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.right: parent.right
spacing: Theme.spacingM
DankIcon {
name: "visibility_off"
size: 14
color: Theme.outline
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: I18n.tr("Hidden")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.outline
anchors.verticalCenter: parent.verticalCenter
}
Rectangle {
width: parent.width - x
height: 1
color: Theme.outline
opacity: 0.2
anchors.verticalCenter: parent.verticalCenter
}
}
}
Repeater {
model: root.tabIds
delegate: Item {
id: rowItem
required property int index
required property string modelData
readonly property var present: root.presentationFor(modelData)
readonly property bool isEnabled: root.isEnabled(modelData)
readonly property bool dragging: root.draggingId === modelData
readonly property bool highlighted: root.highlightedId === modelData
readonly property bool canHide: root.canHide(modelData)
width: reorderArea.width
height: root.rowHeight
z: dragging ? 100 : (highlighted ? 3 : 1)
Binding {
target: rowItem
property: "y"
value: root.slotYForId(rowItem.modelData)
when: !rowItem.dragging
restoreMode: Binding.RestoreNone
}
onYChanged: {
if (dragging)
root.updateDragTarget(y + height / 2);
}
Behavior on y {
enabled: !rowItem.dragging
NumberAnimation {
duration: Theme.expressiveDurations.expressiveDefaultSpatial
easing.type: Easing.BezierSpline
easing.bezierCurve: Theme.expressiveCurves.expressiveFastSpatial
}
}
Item {
id: content
anchors.fill: parent
scale: rowItem.dragging ? 1.02 : 1.0
transformOrigin: Item.Center
Behavior on scale {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
Rectangle {
id: surface
anchors.fill: parent
radius: rowItem.dragging ? Theme.cornerRadius + 6 : Theme.cornerRadius
color: {
if (rowItem.dragging)
return Theme.secondaryContainer;
const base = Theme.surfaceContainer;
return Qt.rgba(base.r, base.g, base.b, rowItem.isEnabled ? 0.7 : 0.4);
}
border.width: rowItem.dragging ? 2 : 1
border.color: rowItem.dragging ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
Behavior on radius {
NumberAnimation {
duration: Theme.shortDuration
easing.type: Easing.OutCubic
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
Behavior on border.color {
ColorAnimation {
duration: Theme.shortDuration
}
}
Rectangle {
anchors.fill: parent
radius: parent.radius
color: Theme.primary
opacity: (dragArea.containsMouse && !rowItem.dragging) ? 0.06 : 0
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
}
}
}
DankIcon {
id: dragHandle
name: "drag_indicator"
size: Theme.iconSize - 4
color: rowItem.dragging ? Theme.primary : Theme.outline
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
opacity: rowItem.isEnabled ? ((dragArea.containsMouse || rowItem.dragging || rowItem.highlighted) ? 1.0 : 0.45) : 0
visible: opacity > 0.01
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
}
}
}
DankIcon {
id: tabIcon
name: rowItem.present.icon
size: Theme.iconSize
color: rowItem.isEnabled ? Theme.primary : Theme.outline
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM * 2 + Theme.iconSize - 4
anchors.verticalCenter: parent.verticalCenter
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
}
}
}
Column {
anchors.left: tabIcon.right
anchors.leftMargin: Theme.spacingM
anchors.right: visibilityButton.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: 2
StyledText {
text: rowItem.present.text
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: rowItem.isEnabled ? Theme.surfaceText : Theme.outline
elide: Text.ElideRight
width: parent.width
}
StyledText {
text: rowItem.present.description
font.pixelSize: Theme.fontSizeSmall
color: rowItem.isEnabled ? Theme.outline : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6)
elide: Text.ElideRight
width: parent.width
visible: text.length > 0
}
}
DankActionButton {
id: visibilityButton
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
buttonSize: 36
iconName: rowItem.isEnabled ? "visibility" : "visibility_off"
iconSize: 18
iconColor: rowItem.isEnabled ? Theme.primary : Theme.outline
enabled: rowItem.canHide
onClicked: {
root.forceActiveFocus();
root.highlightedId = rowItem.modelData;
SettingsData.setDashTabEnabled(rowItem.modelData, !rowItem.isEnabled);
}
}
}
}
Rectangle {
anchors.fill: parent
anchors.margins: -2
radius: Theme.cornerRadius + 2
color: "transparent"
border.width: 2
border.color: Theme.primary
opacity: rowItem.highlighted && !rowItem.dragging ? 0.6 : 0
visible: opacity > 0.01
Behavior on opacity {
NumberAnimation {
duration: Theme.shortDuration
}
}
}
MouseArea {
id: dragArea
anchors.fill: parent
anchors.rightMargin: 48
hoverEnabled: true
enabled: rowItem.isEnabled
cursorShape: rowItem.dragging ? Qt.ClosedHandCursor : Qt.OpenHandCursor
drag.target: rowItem
drag.axis: Drag.YAxis
drag.minimumY: -rowItem.height
drag.maximumY: reorderArea.height
drag.smoothed: false
onPressed: {
root.forceActiveFocus();
root.highlightedId = rowItem.modelData;
root.beginDrag(rowItem.modelData);
}
onReleased: root.endDrag()
}
}
}
}
}
}
}
}
@@ -178,6 +178,7 @@ TAB_CATEGORY_MAP = {
39: "Network", 39: "Network",
40: "Network", 40: "Network",
41: "Network", 41: "Network",
43: "Dank Dash",
} }
SEARCHABLE_COMPONENTS = [ SEARCHABLE_COMPONENTS = [
@@ -9124,5 +9124,16 @@
"settings" "settings"
], ],
"icon": "battery_charging_full" "icon": "battery_charging_full"
},
{
"section": "_tab_43",
"label": "Dank Dash",
"tabIndex": 43,
"category": "Dank Dash",
"keywords": [
"dank",
"dash"
],
"icon": "space_dashboard"
} }
] ]