diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml new file mode 100644 index 00000000..3a756528 --- /dev/null +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -0,0 +1,67 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell.Io +import Quickshell.Widgets + +TabButton { + id: button + property string buttonText + property string buttonIcon + property bool selected: false + property int tabContentWidth: contentItem.children[0].implicitWidth + height: buttonBackground.height + + PointingHandInteraction {} + + background: Rectangle { + id: buttonBackground + radius: Appearance.rounding.small + implicitHeight: 50 + color: (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + + Behavior on color { + ColorAnimation { + duration: Appearance.animation.elementDecel.duration + easing.type: Appearance.animation.elementDecel.type + } + } + } + contentItem: Item { + anchors.centerIn: buttonBackground + ColumnLayout { + anchors.centerIn: parent + spacing: 0 + MaterialSymbol { + visible: buttonIcon?.length > 0 + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + text: buttonIcon + font.pixelSize: 24 + color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 + Behavior on color { + ColorAnimation { + duration: Appearance.animation.elementDecel.duration + easing.type: Appearance.animation.elementDecel.type + } + } + } + StyledText { + id: buttonTextWidget + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Appearance.font.pixelSize.small + color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 + text: buttonText + Behavior on color { + ColorAnimation { + duration: Appearance.animation.elementDecel.duration + easing.type: Appearance.animation.elementDecel.type + } + } + } + } + } +} \ No newline at end of file diff --git a/.config/quickshell/modules/common/widgets/StyledTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml similarity index 84% rename from .config/quickshell/modules/common/widgets/StyledTabButton.qml rename to .config/quickshell/modules/common/widgets/SecondaryTabButton.qml index 40d89c42..9a8f7e16 100644 --- a/.config/quickshell/modules/common/widgets/StyledTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -12,6 +12,7 @@ TabButton { property string buttonIcon property bool selected: false height: buttonBackground.height + property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2 PointingHandInteraction {} @@ -27,9 +28,6 @@ TabButton { easing.type: Appearance.animation.elementDecel.type } } - - // border.color: button.activeFocus ? Appearance.m3colors.m3secondary : Appearance.transparentize(Appearance.m3colors.m3secondary, 1) - // border.width: button.activeFocus ? 2 : 0 } contentItem: Item { anchors.centerIn: buttonBackground @@ -37,9 +35,11 @@ TabButton { anchors.centerIn: parent spacing: 0 MaterialSymbol { + visible: buttonIcon?.length > 0 Layout.rightMargin: 5 + verticalAlignment: Text.AlignVCenter text: buttonIcon - font.pixelSize: Appearance.font.pixelSize.larger + font.pixelSize: Appearance.font.pixelSize.huge color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 Behavior on color { ColorAnimation { @@ -50,9 +50,10 @@ TabButton { } StyledText { id: buttonTextWidget - horizontalAlignment: Text.AlignHCenter - text: buttonText + verticalAlignment: Text.AlignVCenter + font.pixelSize: Appearance.font.pixelSize.small color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 + text: buttonText Behavior on color { ColorAnimation { duration: Appearance.animation.elementDecel.duration diff --git a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml index a7d80202..0c452829 100644 --- a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml @@ -94,7 +94,7 @@ Rectangle { property int remainingTasks: Todo.list.filter(task => !task.done).length; Layout.margins: 10 Layout.leftMargin: 0 - text: `${DateTime.day} ${DateTime.month} ${DateTime.year} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}` + text: `${DateTime.day} ${DateTime.month} ${DateTime.year} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}` font.pixelSize: Appearance.font.pixelSize.large color: Appearance.colors.colOnLayer1 } diff --git a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml new file mode 100644 index 00000000..fcc4ddf4 --- /dev/null +++ b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml @@ -0,0 +1,110 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/services" +import "./calendar" +import "./todo" +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell + +Rectangle { + id: root + radius: Appearance.rounding.normal + color: Appearance.colors.colLayer1 + + property int currentTab: 0 + property var tabButtonList: [{"icon": "notifications", "name": "Notifications"}, {"icon": "volume_up", "name": "Volume mixer"}] + + ColumnLayout { + anchors.margins: 5 + anchors.fill: parent + spacing: 0 + + TabBar { + id: tabBar + Layout.fillWidth: true + currentIndex: currentTab + onCurrentIndexChanged: currentTab = currentIndex + + background: Item { + WheelHandler { + onWheel: (event) => { + if (event.angleDelta.y < 0) + tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1) + else if (event.angleDelta.y > 0) + tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0) + } + acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad + } + } + + Repeater { + model: root.tabButtonList + delegate: PrimaryTabButton { + selected: (index == currentTab) + buttonText: modelData.name + buttonIcon: modelData.icon + } + } + } + + Item { // Tab indicator + id: tabIndicator + Layout.fillWidth: true + height: 3 + property bool enableIndicatorAnimation: false + Connections { + target: root + function onCurrentTabChanged() { + tabIndicator.enableIndicatorAnimation = true + } + } + Rectangle { + color: Appearance.m3colors.m3primary + radius: Appearance.rounding.full + z: 2 + + anchors.fill: parent + anchors.leftMargin: { + const tabCount = root.tabButtonList.length + const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + const fullTabSize = tabBar.width / tabCount; + return fullTabSize * currentTab + (fullTabSize - targetWidth) / 2; + } + anchors.rightMargin: { + const tabCount = root.tabButtonList.length + const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + const fullTabSize = tabBar.width / tabCount; + return fullTabSize * (tabCount - currentTab - 1) + (fullTabSize - targetWidth) / 2; + } + Behavior on anchors.leftMargin { + enabled: tabIndicator.enableIndicatorAnimation + SmoothedAnimation { + velocity: Appearance.animation.positionShift.velocity + } + } + Behavior on anchors.rightMargin { + enabled: tabIndicator.enableIndicatorAnimation + SmoothedAnimation { + velocity: Appearance.animation.positionShift.velocity + } + } + + } + } + + SwipeView { + id: swipeView + Layout.topMargin: 10 + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + currentIndex: currentTab + onCurrentIndexChanged: currentTab = currentIndex + + Item{} + Item{} + } + } +} \ No newline at end of file diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index 717a2923..ca93bef4 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -140,12 +140,10 @@ Scope { } // Center widget group - Rectangle { + CenterWidgetGroup { Layout.alignment: Qt.AlignHCenter Layout.fillHeight: true Layout.fillWidth: true - radius: Appearance.rounding.normal - color: Appearance.colors.colLayer1 } BottomWidgetGroup { diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index 88336d6a..0fbc927a 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -50,9 +50,9 @@ Item { WheelHandler { onWheel: (event) => { if (event.angleDelta.y < 0) - currentTab = Math.min(currentTab + 1, root.tabButtonList.length - 1) + tabBar.currentIndex = Math.min(tabBar.currentIndex + 1, root.tabButtonList.length - 1) else if (event.angleDelta.y > 0) - currentTab = Math.max(currentTab - 1, 0) + tabBar.currentIndex = Math.max(tabBar.currentIndex - 1, 0) } acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad } @@ -60,7 +60,7 @@ Item { Repeater { model: root.tabButtonList - delegate: StyledTabButton { + delegate: SecondaryTabButton { selected: (index == currentTab) buttonText: modelData.name buttonIcon: modelData.icon @@ -68,21 +68,47 @@ Item { } } - Item { + Item { // Tab indicator + id: tabIndicator Layout.fillWidth: true + height: 3 + property bool enableIndicatorAnimation: false + Connections { + target: root + function onCurrentTabChanged() { + tabIndicator.enableIndicatorAnimation = true + } + } Rectangle { - property int indicatorPadding: 15 - id: indicator color: Appearance.m3colors.m3primary - height: 3 radius: Appearance.rounding.full - - width: tabBar.width / root.tabButtonList.length - indicatorPadding * 2 - x: indicatorPadding + tabBar.width / root.tabButtonList.length * currentTab z: 2 - Behavior on x { SmoothedAnimation { - velocity: Appearance.animation.positionShift.velocity - } } + + anchors.fill: parent + anchors.leftMargin: { + const tabCount = root.tabButtonList.length + const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + const fullTabSize = tabBar.width / tabCount; + return fullTabSize * currentTab + (fullTabSize - targetWidth) / 2; + } + anchors.rightMargin: { + const tabCount = root.tabButtonList.length + const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + const fullTabSize = tabBar.width / tabCount; + return fullTabSize * (tabCount - currentTab - 1) + (fullTabSize - targetWidth) / 2; + } + Behavior on anchors.leftMargin { + enabled: tabIndicator.enableIndicatorAnimation + SmoothedAnimation { + velocity: Appearance.animation.positionShift.velocity + } + } + Behavior on anchors.rightMargin { + enabled: tabIndicator.enableIndicatorAnimation + SmoothedAnimation { + velocity: Appearance.animation.positionShift.velocity + } + } } }