publish: the public projection begins here
This is a projection, not a development branch. The tree above was constructed from the internal source named below under a manifest that decides which paths may leave, then scanned as a whole tree rather than as a series of patches, and only then published. Public history starts here because the history before it was not admissible, and neither was the tree. What used to stand in this repository included a rescue copy of another machine, a directory of phone handoffs, deployment wired to one house, and a submodule pointing at a forge no stranger can reach. None of that was ever the product. It stays in the private forge, which is allowed to hold the whole working organism, and this is what was deliberately sent out instead. Three mechanisms produced this tree, in decreasing order of trust. A top-level path the manifest does not name never arrives at all, which is the one that catches directories nobody has thought of yet. Named internal files inside admitted roots are dropped. A short, reviewed table replaces deployment defaults that a public build must not carry -- an endpoint aimed at one LAN, a VPN profile belonging to one phone, packaging built from one checkout path. Everything after this commit is an ordinary publication with the same three trailers, so a force push stops being routine and starts meaning that something deliberate happened. The trailers bind the projection to its source without pretending the public SHA is the private one: same lineage, different tree, and the record says so. Source-Sha: 8f27b1e76a8fef560a336aba18e6990713ff1047 Policy-Sha: 6b261d2f3e6e1fb19874846ba4bb1dfe15565d25b8618c1c1afba0419c101d27 Tree-Digest: 18ec3563c5e5ef9a414993a9f6734b251ff9ed3cd56eebdd6cac01e45c6e3067
This commit is contained in:
commit
8f42fc953d
1476 changed files with 238455 additions and 0 deletions
37
surfaces/quickshell/ii-base/modules/waffle/README.md
Normal file
37
surfaces/quickshell/ii-base/modules/waffle/README.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
## Waffle
|
||||
|
||||
A recreation of Windoes. It's WIP!
|
||||
|
||||
- If you install illogical-impulse fully, you can press Super+Alt+W to switch to this style.
|
||||
- If you're just copying the Quickshell config, run the config as usual (`qs -c ii`) then run `qs -c ii ipc call panelFamily cycle`
|
||||
|
||||
## From EWW version to Quickshell
|
||||
|
||||
Just a reflection, in case anyone's interested. My blog is probably a better place for this, but it does not exist. Besides, this is going to change as I do more stuff. Currently there's just the bar.
|
||||
|
||||
### Improvements
|
||||
|
||||
- QtQuick's `Button` has the `{top/bottom/left/right}Inset` properties, so we can have clickable regions expanding beyond the button background for free. With EWW it was annoying to wrap the button content with an `eventbox` that has some padding, then somehow use CSS selectors to make sure hovering effects work. I have to admit, (a large) part of that annoyance was with how bad my copy-pasting coding practice was at the time, but still...
|
||||
|
||||
- Fancy effects: Gtk3 CSS does not support transformations. In QtQuick we can smack `rotation` and `scale` almost everywhere, so it's simple to make bouncy icons and rotating chevrons
|
||||
|
||||
- Quickshell provides a system tray service (EWW does now but didn't at the time I created the EWW Windoes version), so now there's no Waybar needed for the tray.
|
||||
|
||||
- QtQuick has `Loader`s, so we can have this live-switchable from the main style without killing the widget system, moving styles to the correct folder, and relaunching.
|
||||
|
||||
- This time my computer is powerful enough to run a VM, so I don't have to occasionally reboot to take quick screenshots for reference. I try to make everything pixel-perfect so this is necessary. Speaking about pixel-perfectness, in the EWW version I hardcoded sizes, but this time I'm still doing that (lol), BUT that's normal and not a problem because Qt has the `QT_SCALE_FACTOR` env var for scaling. (Please feel free to prove me wrong in saying Gtk3 doesn't have that magic)
|
||||
|
||||
### Challenges
|
||||
|
||||
- Qt is not Gtk and definitely not React
|
||||
- We don't get directional border on QtQuick `Rectangle`s like in CSS. I was able to get around this with manual drawing, but it was a bit more work
|
||||
|
||||
- Fluent Icons is difficult to use, compared to Material Symbols
|
||||
- No React, so no clean use via a library.
|
||||
- If we use the font, there's no proper, searchable **codepoint** cheatsheet like Nerd Fonts, and there's no ligatures
|
||||
- I resorted to downloading individual SVGs. Not that nice, but it's better than scanning the whole table of icons every time I want one. For this we have fluenticon.com and fluenticons.co, but icons are awkwardly named and there's no alias. Why is the reload/refresh icon called "arrow-sync"? Well, the name is not misleading, but arguably reload/refresh are more common actions. From Fluent Design's [page on Iconography](https://fluent2.microsoft.design/iconography):
|
||||
|
||||
> Fluent system icons are literal metaphors and are named for the shape or object they represent, not the functionality they provide
|
||||
|
||||
"sync" is functionality.
|
||||
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter.mainPage
|
||||
|
||||
WBarAttachedPanelContent {
|
||||
id: root
|
||||
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
// This somewhat sophisticated anchoring is needed to make opening anim not jump abruptly when stuff appear
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.barAtBottom ? undefined : parent.top
|
||||
bottom: root.barAtBottom ? parent.bottom : undefined
|
||||
margins: root.visualMargin
|
||||
bottomMargin: 0
|
||||
}
|
||||
spacing: 12
|
||||
|
||||
WPane {
|
||||
opacity: (MprisController.activePlayer != null && MprisController.isRealPlayer(MprisController.activePlayer)) ? 1 : 0
|
||||
Layout.fillWidth: true
|
||||
contentItem: MediaPaneContent {}
|
||||
}
|
||||
WPane {
|
||||
Layout.fillWidth: true
|
||||
contentItem: WStackView {
|
||||
id: stackView
|
||||
implicitWidth: initItem.implicitWidth
|
||||
implicitHeight: initItem.implicitHeight
|
||||
|
||||
initialItem: WPanelPageColumn {
|
||||
id: initItem
|
||||
MainPageBody {}
|
||||
WPanelSeparator {}
|
||||
MainPageFooter {}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
ActionCenterContext.stackView = this;
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.BackButton
|
||||
onClicked: {
|
||||
ActionCenterContext.back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property StackView stackView
|
||||
|
||||
function push(component) {
|
||||
if (stackView) {
|
||||
stackView.push(component)
|
||||
}
|
||||
}
|
||||
|
||||
function back() {
|
||||
if (stackView && stackView.depth > 1) {
|
||||
stackView.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
WChoiceButton {
|
||||
id: root
|
||||
|
||||
property bool expanded: false
|
||||
checked: expanded
|
||||
clip: true
|
||||
|
||||
horizontalPadding: 12
|
||||
verticalPadding: 6
|
||||
animateChoiceHighlight: false
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
onClicked: expanded = !expanded
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
required property string title
|
||||
spacing: 4
|
||||
|
||||
WPanelIconButton {
|
||||
iconName: "arrow-left"
|
||||
onClicked: ActionCenterContext.back()
|
||||
}
|
||||
|
||||
WText {
|
||||
id: titleText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.title
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
implicitHeight: 176
|
||||
implicitWidth: 358
|
||||
color: Looks.colors.bgPanelBody
|
||||
anchors.fill: parent
|
||||
|
||||
readonly property var activePlayer: MprisController.activePlayer
|
||||
|
||||
Column {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 23
|
||||
rightMargin: 23
|
||||
topMargin: 16
|
||||
bottomMargin: 20
|
||||
}
|
||||
spacing: 25
|
||||
|
||||
AppInfoRow {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
TrackInfoRow {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
}
|
||||
|
||||
ControlButtonsRow {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
}
|
||||
|
||||
component AppInfoRow: RowLayout {
|
||||
id: appInfo
|
||||
spacing: 8
|
||||
|
||||
property var desktopEntry: {
|
||||
const desktopEntryString = root.activePlayer?.desktopEntry ?? "";
|
||||
return DesktopEntries.byId(desktopEntryString);
|
||||
}
|
||||
|
||||
FluentIcon {
|
||||
implicitSize: 20
|
||||
icon: appInfo.desktopEntry?.icon || "music-note-2"
|
||||
monochrome: !appInfo.desktopEntry?.icon
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
text: appInfo.desktopEntry?.name ?? Translation.tr("Media")
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
component TrackInfoRow: RowLayout {
|
||||
spacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
id: trackInfo
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
font.weight: Looks.font.weight.strong
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
elide: Text.ElideRight
|
||||
text: StringUtils.cleanMusicTitle(root.activePlayer?.trackTitle) || Translation.tr("Unknown Title")
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.activePlayer?.trackArtist || Translation.tr("Unknown Artist")
|
||||
}
|
||||
}
|
||||
|
||||
StyledImage {
|
||||
id: artImage
|
||||
Layout.preferredWidth: 58
|
||||
Layout.preferredHeight: trackInfo.implicitHeight
|
||||
source: MprisController.activeTrack?.artUrl || ""
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Item {
|
||||
width: artImage.width
|
||||
height: artImage.height
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: artImage.paintedWidth
|
||||
height: artImage.paintedHeight
|
||||
radius: Looks.radius.medium
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ControlButtonsRow: RowLayout {
|
||||
spacing: 26
|
||||
|
||||
MediaControlButton {
|
||||
iconName: "previous"
|
||||
enabled: root.activePlayer?.canGoPrevious ?? false
|
||||
onClicked: root.activePlayer?.previous()
|
||||
}
|
||||
MediaControlButton {
|
||||
readonly property bool playing: root.activePlayer?.isPlaying ?? false
|
||||
iconName: playing ? "pause" : "play"
|
||||
enabled: (playing && root.activePlayer?.canPause) || (!playing && root.activePlayer?.canPlay)
|
||||
onClicked: root.activePlayer?.togglePlaying()
|
||||
}
|
||||
MediaControlButton {
|
||||
iconName: "next"
|
||||
enabled: root.activePlayer?.canGoNext ?? false
|
||||
onClicked: root.activePlayer?.next()
|
||||
}
|
||||
}
|
||||
|
||||
component MediaControlButton: WBorderlessButton {
|
||||
id: controlButton
|
||||
required property string iconName
|
||||
implicitHeight: 40
|
||||
implicitWidth: 40
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: controlButton.iconName
|
||||
monochrome: true
|
||||
filled: true
|
||||
implicitSize: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WText {
|
||||
Layout.leftMargin: 12
|
||||
Layout.rightMargin: 12
|
||||
Layout.topMargin: 6
|
||||
Layout.bottomMargin: 6
|
||||
|
||||
font {
|
||||
weight: Looks.font.weight.stronger
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
required property string name
|
||||
property alias description: descriptionText.text
|
||||
property alias iconName: iconWidget.icon
|
||||
property alias checked: switchWidget.checked
|
||||
|
||||
spacing: 10
|
||||
|
||||
FluentIcon {
|
||||
id: iconWidget
|
||||
visible: !!root.iconName
|
||||
Layout.leftMargin: 12
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
icon: root.iconName
|
||||
implicitSize: 18
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
spacing: 1
|
||||
|
||||
// Name
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: root.name
|
||||
}
|
||||
// Description
|
||||
WText {
|
||||
id: descriptionText
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.Wrap
|
||||
color: Looks.colors.subfg
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
Layout.rightMargin: 12
|
||||
implicitWidth: switchRow.implicitWidth
|
||||
implicitHeight: switchRow.implicitHeight
|
||||
onPressed: switchWidget.down = true
|
||||
onReleased: switchWidget.down = false
|
||||
onClicked: switchWidget.checked = !switchWidget.checked
|
||||
|
||||
Row {
|
||||
id: switchRow
|
||||
spacing: 12
|
||||
|
||||
WTextWithFixedWidth {
|
||||
longestText: "Off" // The larger one
|
||||
text: switchWidget.checked ? Translation.tr("On") : Translation.tr("Off")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
|
||||
WSwitch {
|
||||
id: switchWidget
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
|
||||
function onSidebarLeftOpenChanged() {
|
||||
if (GlobalStates.sidebarLeftOpen)
|
||||
panelLoader.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: panelLoader
|
||||
active: GlobalStates.sidebarLeftOpen
|
||||
sourceComponent: PanelWindow {
|
||||
id: panelWindow
|
||||
exclusiveZone: 0
|
||||
WlrLayershell.namespace: "quickshell:actionCenter"
|
||||
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
|
||||
color: "transparent"
|
||||
|
||||
anchors {
|
||||
bottom: Config.options.waffles.bar.bottom
|
||||
top: !Config.options.waffles.bar.bottom
|
||||
right: true
|
||||
}
|
||||
|
||||
implicitWidth: content.implicitWidth
|
||||
implicitHeight: content.implicitHeight
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
active: true
|
||||
windows: [panelWindow]
|
||||
onCleared: content.close()
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: GlobalStates
|
||||
function onSidebarLeftOpenChanged() {
|
||||
if (!GlobalStates.sidebarLeftOpen)
|
||||
content.close();
|
||||
}
|
||||
}
|
||||
|
||||
ActionCenterContent {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
|
||||
onClosed: {
|
||||
GlobalStates.sidebarLeftOpen = false;
|
||||
panelLoader.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toggleOpen() {
|
||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "sidebarLeft"
|
||||
|
||||
function toggle() {
|
||||
root.toggleOpen();
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "sidebarLeftToggle"
|
||||
description: "Toggles left sidebar on press"
|
||||
|
||||
onPressed: root.toggleOpen()
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "mediaControls"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "mediaControlsToggle"
|
||||
description: "Toggles media controls on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
if (Bluetooth.defaultAdapter.enabled) Bluetooth.defaultAdapter.discovering = true;
|
||||
}
|
||||
Component.onDestruction: {
|
||||
Bluetooth.defaultAdapter.discovering = false;
|
||||
}
|
||||
|
||||
WPanelPageColumn {
|
||||
anchors.fill: parent
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 400
|
||||
implicitWidth: 50
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
ColumnLayout {
|
||||
implicitHeight: headerRow.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
HeaderRow {
|
||||
id: headerRow
|
||||
Layout.fillWidth: true
|
||||
title: Translation.tr("Bluetooth")
|
||||
}
|
||||
WSwitch {
|
||||
id: toggleSwitch
|
||||
Layout.rightMargin: 12
|
||||
checked: Bluetooth.defaultAdapter?.enabled ?? false
|
||||
onCheckedChanged: {
|
||||
if (Bluetooth.defaultAdapter) {
|
||||
Bluetooth.defaultAdapter.enabled = checked;
|
||||
if (checked) {
|
||||
Bluetooth.defaultAdapter.discovering = true;
|
||||
} else {
|
||||
Bluetooth.defaultAdapter.discovering = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
Layout.leftMargin: -4
|
||||
Layout.rightMargin: -4
|
||||
Layout.fillWidth: true
|
||||
shown: Bluetooth.defaultAdapter?.discovering ?? false
|
||||
visible: true
|
||||
sourceComponent: WIndeterminateProgressBar {}
|
||||
}
|
||||
}
|
||||
|
||||
StyledListView {
|
||||
id: listView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
animateAppearance: false
|
||||
|
||||
contentHeight: contentLayout.implicitHeight
|
||||
contentWidth: width
|
||||
clip: true
|
||||
spacing: 4
|
||||
|
||||
model: ScriptModel {
|
||||
values: BluetoothStatus.friendlyDeviceList
|
||||
}
|
||||
delegate: BluetoothDeviceItem {
|
||||
required property BluetoothDevice modelData
|
||||
device: modelData
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {
|
||||
WTextButton {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
text: Translation.tr("More Bluetooth settings")
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.bluetooth]);
|
||||
}
|
||||
}
|
||||
WBorderlessButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
enabled: !Bluetooth.defaultAdapter?.discovering && Bluetooth.defaultAdapter?.enabled
|
||||
|
||||
onClicked: {
|
||||
Bluetooth.defaultAdapter.discovering = true;
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
icon: "arrow-counterclockwise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
ExpandableChoiceButton {
|
||||
id: root
|
||||
required property BluetoothDevice device
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: contentItem
|
||||
spacing: 20
|
||||
|
||||
// Device icon
|
||||
FluentIcon {
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
icon: WIcons.bluetoothDeviceIcon(root?.device)
|
||||
implicitSize: 18
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
|
||||
WText {
|
||||
// Network name
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: root.device?.name || Translation.tr("Unknown device")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
WText { // Status
|
||||
id: statusText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: Looks.colors.subfg
|
||||
visible: root.device?.connected || root.expanded
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
text: {
|
||||
if (!root.device?.paired)
|
||||
return Translation.tr("Not connected");
|
||||
let statusText = root.device?.connected ? Translation.tr("Connected") : Translation.tr("Paired");
|
||||
if (!root.device?.batteryAvailable)
|
||||
return statusText;
|
||||
statusText += ` • ${Math.round(root.device?.battery * 100)}%`;
|
||||
return statusText;
|
||||
}
|
||||
}
|
||||
|
||||
WButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: root.expanded
|
||||
checked: !(root.device?.connected ?? false)
|
||||
colBackground: Looks.colors.bg2
|
||||
colBackgroundHover: Looks.colors.bg2Hover
|
||||
colBackgroundActive: Looks.colors.bg2Active
|
||||
implicitHeight: 30
|
||||
implicitWidth: 148
|
||||
text: root.device?.connected ? Translation.tr("Disconnect") : Translation.tr("Connect")
|
||||
|
||||
onClicked: {
|
||||
if (root.device?.connected) {
|
||||
root.device.disconnect();
|
||||
} else {
|
||||
root.device.connect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
BodyRectangle {
|
||||
id: root
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
|
||||
ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
MainPageBodyToggles {
|
||||
id: togglesContainer
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
implicitHeight: 1
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bg1Border
|
||||
}
|
||||
|
||||
MainPageBodySliders {
|
||||
Layout.margins: 12
|
||||
Layout.topMargin: 18
|
||||
Layout.bottomMargin: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
import qs.modules.waffle.actionCenter.volumeControl
|
||||
|
||||
ColumnLayout {
|
||||
id: root
|
||||
property var screen: root.QsWindow.window?.screen
|
||||
property var brightnessMonitor: Brightness.getMonitorForScreen(screen)
|
||||
spacing: 12
|
||||
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
|
||||
WPanelIconButton {
|
||||
color: colBackground
|
||||
property real animationValue: root.brightnessMonitor?.brightness ?? 0
|
||||
rotation: animationValue * 180
|
||||
scale: 0.8 + animationValue * 0.2
|
||||
iconName: "weather-sunny"
|
||||
|
||||
Behavior on animationValue {
|
||||
animation: Looks.transition.longMovement.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
WSlider {
|
||||
Layout.fillWidth: true
|
||||
value: root.brightnessMonitor?.brightness ?? 0
|
||||
scrollable: true
|
||||
onMoved: {
|
||||
root.brightnessMonitor?.setBrightness(value)
|
||||
}
|
||||
}
|
||||
|
||||
WPanelIconButton {
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: 4
|
||||
|
||||
WPanelIconButton {
|
||||
iconName: WIcons.volumeIcon
|
||||
onClicked: Audio.toggleMute();
|
||||
}
|
||||
|
||||
WSlider {
|
||||
Layout.fillWidth: true
|
||||
value: Audio.sink.audio.volume
|
||||
scrollable: true
|
||||
onMoved: {
|
||||
Audio.sink.audio.volume = value;
|
||||
}
|
||||
}
|
||||
|
||||
WPanelIconButton {
|
||||
Component {
|
||||
id: volumeControlComp
|
||||
VolumeControl {}
|
||||
}
|
||||
onClicked: {
|
||||
ActionCenterContext.push(volumeControlComp)
|
||||
}
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: -1
|
||||
FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitSize: 18
|
||||
icon: "options"
|
||||
}
|
||||
FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
implicitSize: 12
|
||||
icon: "chevron-right"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import Qt.labs.synchronizer
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.models.quickToggles
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter.toggles
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int columns: 3
|
||||
property int rows: 2
|
||||
property int currentPage: 0
|
||||
readonly property int itemsPerPage: columns * rows
|
||||
readonly property int pages: Math.ceil(toggles.length / itemsPerPage)
|
||||
property list<string> toggles: Config.options.waffles.actionCenter.toggles
|
||||
|
||||
property real padding: 22
|
||||
property real reducedBottomPadding: 12
|
||||
implicitHeight: swipeView.implicitHeight + (padding - swipeView.padding) * 2 - reducedBottomPadding
|
||||
|
||||
function togglesInPage(index) {
|
||||
var start = index * root.itemsPerPage;
|
||||
var end = start + root.itemsPerPage;
|
||||
return root.toggles.slice(start, end);
|
||||
}
|
||||
|
||||
function decreasePage() {
|
||||
if (root.currentPage > 0) {
|
||||
root.currentPage -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
function increasePage() {
|
||||
if (root.currentPage < (root.pages - 1)) {
|
||||
root.currentPage += 1;
|
||||
}
|
||||
}
|
||||
|
||||
clip: true
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: root.padding - swipeView.padding
|
||||
bottomMargin: root.padding - swipeView.padding - root.reducedBottomPadding
|
||||
}
|
||||
padding: 4
|
||||
leftPadding: root.padding
|
||||
rightPadding: root.padding
|
||||
spacing: padding
|
||||
|
||||
orientation: Qt.Vertical
|
||||
clip: true
|
||||
Synchronizer on currentIndex {
|
||||
property alias source: root.currentPage
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.pages
|
||||
delegate: GridLayout {
|
||||
id: grid
|
||||
required property int index
|
||||
// width: SwipeView.view.width - root.padding * 2
|
||||
// height: SwipeView.view.height - root.padding * 2
|
||||
|
||||
columns: root.columns
|
||||
rows: root.rows
|
||||
rowSpacing: 12
|
||||
columnSpacing: 12
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.togglesInPage(grid.index)
|
||||
}
|
||||
delegate: ActionCenterTogglesDelegateChooser {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VerticalPageIndicator {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 6
|
||||
|
||||
currentIndex: root.currentPage
|
||||
count: root.pages
|
||||
onClicked: (index) => root.currentPage = index
|
||||
onIncreasePage: root.increasePage();
|
||||
onDecreasePage: root.decreasePage();
|
||||
}
|
||||
|
||||
FocusedScrollMouseArea {
|
||||
z: 999
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: false
|
||||
onScrollUp: root.decreasePage();
|
||||
onScrollDown: root.increasePage();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
FooterRectangle {
|
||||
|
||||
// Battery button
|
||||
WBorderlessButton {
|
||||
visible: Battery.available
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
|
||||
contentItem: Row {
|
||||
spacing: 4
|
||||
|
||||
FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: WIcons.batteryLevelIcon
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: WIcons.batteryIcon
|
||||
}
|
||||
}
|
||||
WText {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: `${Math.round(Battery.percentage * 100) || 0}%`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Settings button
|
||||
WBorderlessButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
|
||||
onClicked: {
|
||||
GlobalStates.sidebarLeftOpen = false;
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]);
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
icon: "settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Bluetooth
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
if (Bluetooth.defaultAdapter.enabled)
|
||||
Bluetooth.defaultAdapter.discovering = true;
|
||||
}
|
||||
Component.onDestruction: {
|
||||
Bluetooth.defaultAdapter.discovering = false;
|
||||
}
|
||||
|
||||
WPanelPageColumn {
|
||||
anchors.fill: parent
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 400
|
||||
implicitWidth: 50
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
HeaderRow {
|
||||
id: headerRow
|
||||
Layout.fillWidth: true
|
||||
title: Translation.tr("Eye protection")
|
||||
}
|
||||
|
||||
StyledFlickable {
|
||||
id: flickable
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentHeight: contentLayout.implicitHeight
|
||||
contentWidth: width
|
||||
clip: true
|
||||
|
||||
bottomMargin: 12
|
||||
|
||||
NightLightOptions {
|
||||
id: contentLayout
|
||||
width: flickable.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {}
|
||||
}
|
||||
|
||||
component NightLightOptions: ColumnLayout {
|
||||
spacing: 10
|
||||
|
||||
SectionText {
|
||||
text: Translation.tr("Night Light")
|
||||
}
|
||||
|
||||
ToggleItem {
|
||||
name: Translation.tr("Automatic")
|
||||
description: Translation.tr("Turn on from sunset to sunrise")
|
||||
iconName: "auto"
|
||||
checked: Config.options.light.night.automatic
|
||||
onCheckedChanged: {
|
||||
Config.options.light.night.automatic = checked;
|
||||
}
|
||||
}
|
||||
|
||||
ToggleItem {
|
||||
name: Translation.tr("Enable now")
|
||||
description: Translation.tr("More comfortable viewing at night")
|
||||
iconName: WIcons.nightLightIcon
|
||||
checked: Hyprsunset.temperatureActive
|
||||
onCheckedChanged: {
|
||||
Hyprsunset.toggleTemperature(checked);
|
||||
}
|
||||
}
|
||||
|
||||
IntensityEntry {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
SectionText {
|
||||
text: Translation.tr("Anti-flashbang (experimental)")
|
||||
}
|
||||
|
||||
ToggleItem {
|
||||
name: Translation.tr("Enable")
|
||||
description: Translation.tr("Balance brightness based on content")
|
||||
iconName: "flash-off"
|
||||
checked: Config.options.light.antiFlashbang.enable
|
||||
onCheckedChanged: {
|
||||
Config.options.light.antiFlashbang.enable = checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IntensityEntry: RowLayout {
|
||||
spacing: 10
|
||||
|
||||
FluentIcon {
|
||||
id: iconWidget
|
||||
Layout.leftMargin: 12
|
||||
Layout.topMargin: 4
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
icon: "temperature"
|
||||
implicitSize: 18
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
// Layout.leftMargin: 40
|
||||
Layout.rightMargin: 12
|
||||
spacing: 4
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
text: Translation.tr("Intensity")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
text: Translation.tr("Adjust the color temperature")
|
||||
color: Looks.colors.subfg
|
||||
}
|
||||
}
|
||||
WSlider {
|
||||
Layout.fillWidth: true
|
||||
from: 6500
|
||||
to: 1200
|
||||
value: Config.options.light.night.colorTemperature
|
||||
onMoved: Config.options.light.night.colorTemperature = value
|
||||
tooltipContent: Math.round((value - from) / (to - from) * 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models.quickToggles
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
// It should be perfectly fine to use just a Column here, but somehow
|
||||
// using ColumnLayout prevents weird opening anim stutter
|
||||
ColumnLayout {
|
||||
id: root
|
||||
|
||||
required property QuickToggleModel toggleModel
|
||||
property string name: toggleModel?.name ?? ""
|
||||
property string statusText: (toggleModel?.hasStatusText) ? (toggleModel?.statusText || (toggled ? Translation.tr("Active") : Translation.tr("Inactive"))) : ""
|
||||
property string tooltipText: toggleModel?.tooltipText ?? ""
|
||||
required property string icon
|
||||
property bool available: toggleModel?.available ?? true
|
||||
property bool toggled: toggleModel?.toggled ?? false
|
||||
property var mainAction: toggleModel?.mainAction ?? null
|
||||
property var altAction: toggleModel?.hasMenu ? (() => root.openMenu()) : (toggleModel?.altAction ?? null)
|
||||
property bool hasMenu: toggleModel?.hasMenu ?? false
|
||||
property Component menu
|
||||
|
||||
property color colBackground: toggled ? Looks.colors.accent : Looks.colors.bg2
|
||||
property color colBackgroundHovered: toggled ? Looks.colors.accentHover : Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: toggled ? Looks.colors.accentActive : Looks.colors.bg2Active
|
||||
property color colBorder: toggled ? Looks.colors.accentHover : Looks.colors.bg2Border
|
||||
property color colForeground: toggled ? Looks.colors.accentFg : Looks.colors.fg1
|
||||
|
||||
spacing: 0
|
||||
property real wholeToggleWidth: 96
|
||||
|
||||
AcrylicRectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: root.wholeToggleWidth
|
||||
implicitHeight: 48
|
||||
color: root.colBackground
|
||||
radius: Looks.radius.medium
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
ToggleFragment {
|
||||
topLeftRadius: Looks.radius.medium
|
||||
bottomLeftRadius: Looks.radius.medium
|
||||
topRightRadius: root.hasMenu ? 0 : Looks.radius.medium
|
||||
bottomRightRadius: root.hasMenu ? 0 : Looks.radius.medium
|
||||
iconName: root.icon
|
||||
onClicked: root.mainAction && root.mainAction()
|
||||
}
|
||||
FadeLoader {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
shown: root.hasMenu
|
||||
sourceComponent: ToggleFragment {
|
||||
topLeftRadius: 0
|
||||
bottomLeftRadius: 0
|
||||
topRightRadius: Looks.radius.medium
|
||||
bottomRightRadius: Looks.radius.medium
|
||||
iconName: "chevron-right"
|
||||
onClicked: {
|
||||
ActionCenterContext.stackView.push(root.menu)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: toggleNameWidget
|
||||
implicitWidth: root.wholeToggleWidth
|
||||
implicitHeight: 36
|
||||
WText {
|
||||
id: toggleNameText
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
text: root.name
|
||||
}
|
||||
}
|
||||
|
||||
component ToggleFragment: WButton {
|
||||
id: toggleFragment
|
||||
required property string iconName
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
inset: 0
|
||||
backgroundOpacity: 0.8
|
||||
checked: root.toggled
|
||||
border.width: 1
|
||||
border.color: root.colBorder
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: toggleFragment.iconName
|
||||
implicitSize: 18
|
||||
monochrome: true
|
||||
filled: root.toggled
|
||||
color: root.colForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.models.quickToggles
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter.bluetooth
|
||||
import qs.modules.waffle.actionCenter.nightLight
|
||||
import qs.modules.waffle.actionCenter.volumeControl
|
||||
import qs.modules.waffle.actionCenter.wifi
|
||||
|
||||
DelegateChooser {
|
||||
id: root
|
||||
|
||||
// role: "type" is implied by usage
|
||||
|
||||
DelegateChoice {
|
||||
roleValue: "antiFlashbang"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: AntiFlashbangToggle {}
|
||||
icon: "flash-off"
|
||||
menu: Component {
|
||||
NightLightControl {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "bluetooth"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: BluetoothToggle {}
|
||||
name: toggleModel.statusText
|
||||
icon: WIcons.bluetoothIcon
|
||||
menu: Component {
|
||||
BluetoothControl {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "cloudflareWarp"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: CloudflareWarpToggle {}
|
||||
icon: "cloudflare"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "colorPicker"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: ColorPickerToggle {}
|
||||
icon: "eyedropper"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "darkMode"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: DarkModeToggle {}
|
||||
icon: "dark-theme"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "easyEffects"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: EasyEffectsToggle {}
|
||||
icon: "device-eq"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "gameMode"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: GameModeToggle {}
|
||||
icon: "games"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "idleInhibitor"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: IdleInhibitorToggle {}
|
||||
icon: "drink-coffee"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "mic"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: MicToggle {}
|
||||
icon: WIcons.micIcon
|
||||
menu: Component {
|
||||
VolumeControl {
|
||||
output: false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "musicRecognition"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: MusicRecognitionToggle {}
|
||||
icon: "music-note-2"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "network"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: NetworkToggle {}
|
||||
name: toggleModel.statusText
|
||||
icon: WIcons.internetIcon
|
||||
menu: Component {
|
||||
WifiControl {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "nightLight"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: NightLightToggle {}
|
||||
icon: WIcons.nightLightIcon
|
||||
menu: Component {
|
||||
NightLightControl {}
|
||||
}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "notifications"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: NotificationToggle {}
|
||||
icon: WIcons.notificationsIcon
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "onScreenKeyboard"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: OnScreenKeyboardToggle {}
|
||||
icon: GlobalStates.oskOpen ? "keyboard-dock" : "keyboard"
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "powerProfile"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: PowerProfilesToggle {}
|
||||
icon: WIcons.powerProfileIcon
|
||||
name: toggleModel.statusText
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "screenSnip"
|
||||
ActionCenterToggleButton {
|
||||
toggleModel: ScreenSnipToggle {}
|
||||
icon: "cut"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
ActionCenterToggle {
|
||||
id: root
|
||||
|
||||
name: Network.ethernet ? Translation.tr("Network") : Network.networkName
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property bool output: true
|
||||
|
||||
WPanelPageColumn {
|
||||
anchors.fill: parent
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 400
|
||||
implicitWidth: 50
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
HeaderRow {
|
||||
Layout.fillWidth: true
|
||||
title: root.output ? Translation.tr("Sound output") : Translation.tr("Sound input")
|
||||
}
|
||||
|
||||
StyledFlickable {
|
||||
id: flickable
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentHeight: contentLayout.implicitHeight
|
||||
contentWidth: width
|
||||
clip: true
|
||||
|
||||
AudioChoices {
|
||||
id: contentLayout
|
||||
width: flickable.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {
|
||||
WButton {
|
||||
id: moreSettingsButton
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
implicitHeight: 40
|
||||
implicitWidth: contentItem.implicitWidth + 30
|
||||
color: "transparent"
|
||||
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.volumeMixer]);
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
WText {
|
||||
id: buttonText
|
||||
anchors.centerIn: parent
|
||||
text: Translation.tr("More volume settings")
|
||||
color: moreSettingsButton.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component AudioChoices: ColumnLayout {
|
||||
spacing: 4
|
||||
|
||||
SectionText {
|
||||
text: root.output ? Translation.tr("Output device") : Translation.tr("Input device")
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.output ? Audio.outputDevices : Audio.inputDevices
|
||||
}
|
||||
delegate: WChoiceButton {
|
||||
required property var modelData
|
||||
icon.name: WIcons.audioDeviceIcon(modelData)
|
||||
text: Audio.friendlyDeviceName(modelData)
|
||||
checked: (root.output ? Audio.sink : Audio.source) === modelData
|
||||
onClicked: {
|
||||
if (root.output) Audio.setDefaultSink(modelData);
|
||||
else Audio.setDefaultSource(modelData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {
|
||||
visible: EasyEffects.available && root.output
|
||||
color: Looks.colors.bg2Hover
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
SectionText {
|
||||
visible: EasyEffects.available && root.output
|
||||
text: Translation.tr("Sound effects")
|
||||
}
|
||||
|
||||
WChoiceButton {
|
||||
visible: EasyEffects.available && root.output
|
||||
text: Translation.tr("Off")
|
||||
checked: !EasyEffects.active
|
||||
onClicked: EasyEffects.disable()
|
||||
}
|
||||
|
||||
WChoiceButton {
|
||||
visible: EasyEffects.available && root.output
|
||||
text: "EasyEffects"
|
||||
checked: EasyEffects.active
|
||||
onClicked: EasyEffects.enable()
|
||||
}
|
||||
|
||||
WPanelSeparator {
|
||||
color: Looks.colors.bg2Hover
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
SectionText {
|
||||
visible: EasyEffects.available
|
||||
text: Translation.tr("Volume mixer")
|
||||
}
|
||||
|
||||
VolumeEntry {
|
||||
node: root.output ? Audio.sink : Audio.source
|
||||
icon: root.output ? "speaker" : "mic-on"
|
||||
monochrome: true
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.output ? Audio.outputAppNodes : Audio.inputAppNodes
|
||||
}
|
||||
delegate: VolumeEntry {
|
||||
required property var modelData
|
||||
node: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.Pipewire
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
required property PwNode node
|
||||
property alias icon: iconButton.iconName
|
||||
property alias monochrome: iconButton.monochrome
|
||||
monochrome: false
|
||||
|
||||
PwObjectTracker { // Necessary for useful info to be present in 'node'
|
||||
objects: [root.node]
|
||||
}
|
||||
|
||||
WPanelIconButton {
|
||||
id: iconButton
|
||||
iconName: WIcons.audioAppIcon(root.node)
|
||||
onClicked: root.node.audio.muted = !root.node?.audio.muted
|
||||
|
||||
FluentIcon {
|
||||
id: muteIcon
|
||||
visible: root.node?.audio.muted ?? false
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
margins: -1
|
||||
}
|
||||
implicitSize: 16
|
||||
icon: "speaker-mute"
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
extraVisibleCondition: iconButton.shouldShowTooltip
|
||||
text: Audio.appNodeDisplayName(root.node)
|
||||
}
|
||||
}
|
||||
|
||||
WSlider {
|
||||
Layout.fillWidth: true
|
||||
Layout.rightMargin: 10
|
||||
value: root.node?.audio.volume ?? 0
|
||||
onMoved: root.node.audio.volume = value
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
ExpandableChoiceButton {
|
||||
id: root
|
||||
required property WifiAccessPoint wifiNetwork
|
||||
|
||||
contentItem: RowLayout {
|
||||
id: contentItem
|
||||
spacing: 12
|
||||
|
||||
FluentIcon { // Duotone hack
|
||||
Layout.bottomMargin: 2
|
||||
Layout.alignment: Qt.AlignTop
|
||||
property int strength: root.wifiNetwork?.strength ?? 0
|
||||
icon: "wifi-1"
|
||||
implicitSize: 30
|
||||
color: Looks.colors.inactiveIcon
|
||||
|
||||
FluentIcon { // Signal
|
||||
property int strength: root.wifiNetwork?.strength ?? 0
|
||||
icon: WIcons.wifiIconForStrength(strength)
|
||||
implicitSize: 30
|
||||
|
||||
FluentIcon { // Security
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
visible: root?.wifiNetwork?.isSecure ?? false
|
||||
icon: "lock-closed"
|
||||
filled: true
|
||||
implicitSize: 14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.topMargin: statusText.visible ? 4 : 7
|
||||
Layout.bottomMargin: 4
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.fillWidth: true
|
||||
spacing: 1
|
||||
|
||||
Behavior on Layout.topMargin {
|
||||
animation: Looks.transition.move.createObject(this)
|
||||
}
|
||||
|
||||
WText { // Network name
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown")
|
||||
textFormat: Text.PlainText
|
||||
}
|
||||
WText { // Status
|
||||
id: statusText
|
||||
Layout.fillWidth: true
|
||||
elide: Text.ElideRight
|
||||
text: root.wifiNetwork?.active ? Translation.tr("Connected") : root.wifiNetwork?.isSecure ? Translation.tr("Secured") : Translation.tr("Not secured")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: Looks.colors.subfg
|
||||
visible: root.wifiNetwork?.active || root.expanded
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
WButton {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
visible: root.expanded
|
||||
checked: !(root.wifiNetwork?.active ?? false)
|
||||
colBackground: Looks.colors.bg2
|
||||
colBackgroundHover: Looks.colors.bg2Hover
|
||||
colBackgroundActive: Looks.colors.bg2Active
|
||||
implicitHeight: 30
|
||||
implicitWidth: 148
|
||||
text: root.wifiNetwork?.active ? Translation.tr("Disconnect") : Translation.tr("Connect")
|
||||
|
||||
onClicked: {
|
||||
if (root.wifiNetwork?.active) {
|
||||
Network.disconnectWifiNetwork();
|
||||
} else {
|
||||
Network.connectToWifiNetwork(root.wifiNetwork);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.actionCenter
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
Component.onCompleted: {
|
||||
Network.rescanWifi();
|
||||
}
|
||||
|
||||
WPanelPageColumn {
|
||||
anchors.fill: parent
|
||||
|
||||
BodyRectangle {
|
||||
implicitHeight: 400
|
||||
implicitWidth: 50
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 4
|
||||
spacing: 4
|
||||
|
||||
ColumnLayout {
|
||||
implicitHeight: headerRow.implicitHeight
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 0
|
||||
HeaderRow {
|
||||
id: headerRow
|
||||
Layout.fillWidth: true
|
||||
title: Translation.tr("Wi-Fi")
|
||||
}
|
||||
WSwitch {
|
||||
id: toggleSwitch
|
||||
Layout.rightMargin: 12
|
||||
checked: Network.wifiStatus !== "disabled"
|
||||
onCheckedChanged: {
|
||||
Network.enableWifi(checked);
|
||||
Network.rescanWifi();
|
||||
}
|
||||
}
|
||||
}
|
||||
FadeLoader {
|
||||
Layout.leftMargin: -4
|
||||
Layout.rightMargin: -4
|
||||
Layout.fillWidth: true
|
||||
shown: Network.wifiScanning
|
||||
visible: true
|
||||
sourceComponent: WIndeterminateProgressBar {}
|
||||
}
|
||||
}
|
||||
|
||||
StyledListView {
|
||||
id: listView
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
animateAppearance: false
|
||||
|
||||
contentHeight: contentLayout.implicitHeight
|
||||
contentWidth: width
|
||||
clip: true
|
||||
spacing: 4
|
||||
|
||||
model: ScriptModel {
|
||||
values: Network.friendlyWifiNetworks
|
||||
}
|
||||
delegate: WWifiNetworkItem {
|
||||
required property WifiAccessPoint modelData
|
||||
wifiNetwork: modelData
|
||||
width: ListView.view.width
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
FooterRectangle {
|
||||
WTextButton {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
}
|
||||
text: Translation.tr("More Internet settings")
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarLeft", "toggle"]);
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.network]);
|
||||
}
|
||||
}
|
||||
WBorderlessButton {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: 12
|
||||
enabled: !Network.wifiScanning
|
||||
|
||||
onClicked: {
|
||||
Network.rescanWifi();
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
icon: "arrow-counterclockwise"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.widgets.widgetCanvas
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
|
||||
import qs.modules.ii.background.widgets
|
||||
import qs.modules.ii.background.widgets.clock
|
||||
import qs.modules.ii.background.widgets.weather
|
||||
|
||||
Variants {
|
||||
id: root
|
||||
model: Quickshell.screens
|
||||
|
||||
PanelWindow {
|
||||
id: panelRoot
|
||||
required property var modelData
|
||||
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: WlrLayer.Bottom
|
||||
WlrLayershell.namespace: "quickshell:background"
|
||||
anchors {
|
||||
top: true
|
||||
bottom: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
color: "transparent"
|
||||
|
||||
StyledImage {
|
||||
anchors.fill: parent
|
||||
source: Config.options.background.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
}
|
||||
}
|
||||
87
surfaces/quickshell/ii-base/modules/waffle/bar/AppButton.qml
Normal file
87
surfaces/quickshell/ii-base/modules/waffle/bar/AppButton.qml
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BarButton {
|
||||
id: root
|
||||
|
||||
required property string iconName
|
||||
property bool multiple: false
|
||||
property bool separateLightDark: false
|
||||
property alias tryCustomIcon: iconWidget.tryCustomIcon
|
||||
leftInset: 2
|
||||
rightInset: 2
|
||||
implicitWidth: height - topInset - bottomInset + leftInset + rightInset
|
||||
|
||||
property real pressedScale: 5/6
|
||||
|
||||
onDownChanged: {
|
||||
scaleAnim.duration = root.down ? 150 : 200
|
||||
scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut
|
||||
contentItem.scale = root.down ? root.pressedScale : 1 // If/When we do dragging, the scale is 1.25
|
||||
}
|
||||
|
||||
background: Item {
|
||||
id: background
|
||||
BackgroundAcrylicRectangle {
|
||||
id: mainBgRect
|
||||
anchors.fill: parent
|
||||
layer.enabled: root.multiple
|
||||
layer.effect: OpacityMask {
|
||||
invert: true
|
||||
maskSource: Item {
|
||||
width: mainBgRect.width
|
||||
height: mainBgRect.height
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 3
|
||||
radius: mainBgRect.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
anchors.rightMargin: 5
|
||||
active: root.multiple
|
||||
sourceComponent: BackgroundAcrylicRectangle {}
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItem
|
||||
anchors.centerIn: root.background
|
||||
|
||||
implicitHeight: iconWidget.implicitHeight
|
||||
implicitWidth: iconWidget.implicitWidth
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
id: scaleAnim
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
|
||||
WAppIcon {
|
||||
id: iconWidget
|
||||
anchors.centerIn: parent
|
||||
iconName: root.iconName
|
||||
separateLightDark: root.separateLightDark
|
||||
}
|
||||
}
|
||||
|
||||
component BackgroundAcrylicRectangle: AcrylicRectangle {
|
||||
shiny: ((root.hovered && !root.down) || root.checked)
|
||||
color: root.color
|
||||
border.width: 1
|
||||
border.color: root.colBackgroundBorder
|
||||
|
||||
Behavior on border.color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
39
surfaces/quickshell/ii-base/modules/waffle/bar/BarButton.qml
Normal file
39
surfaces/quickshell/ii-base/modules/waffle/bar/BarButton.qml
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
AcrylicButton {
|
||||
id: root
|
||||
|
||||
property var altAction: () => {}
|
||||
property var middleClickAction: () => {}
|
||||
|
||||
Layout.fillHeight: true
|
||||
topInset: 4
|
||||
bottomInset: 4
|
||||
leftInset: 0
|
||||
rightInset: 0
|
||||
horizontalPadding: 8
|
||||
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
onPressed: (event) => {
|
||||
root.down = true;
|
||||
}
|
||||
onReleased: (event) => {
|
||||
root.down = false;
|
||||
}
|
||||
onClicked: (event) => {
|
||||
if (event.button === Qt.LeftButton) root.clicked();
|
||||
if (event.button === Qt.RightButton) root.altAction();
|
||||
if (event.button === Qt.MiddleButton) root.middleClickAction();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
|
||||
BarButton {
|
||||
id: root
|
||||
|
||||
property alias iconName: iconContent.icon
|
||||
property alias iconSource: iconContent.source
|
||||
property alias iconSize: iconContent.implicitSize
|
||||
property alias iconRotation: iconContent.rotation
|
||||
property alias iconMonochrome: iconContent.monochrome
|
||||
property alias iconScale: iconContent.scale
|
||||
property alias tooltipText: tooltip.text
|
||||
property alias overlayingItems: iconContent.data
|
||||
|
||||
implicitWidth: 32
|
||||
|
||||
contentItem: Item {
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: iconContent.implicitWidth
|
||||
implicitHeight: iconContent.implicitHeight
|
||||
|
||||
FluentIcon {
|
||||
id: iconContent
|
||||
anchors.centerIn: parent
|
||||
implicitSize: 16
|
||||
icon: root.iconName
|
||||
monochrome: false
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
id: tooltip
|
||||
extraVisibleCondition: root.shouldShowTooltip && text !== ""
|
||||
}
|
||||
}
|
||||
60
surfaces/quickshell/ii-base/modules/waffle/bar/BarMenu.qml
Normal file
60
surfaces/quickshell/ii-base/modules/waffle/bar/BarMenu.qml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BarPopup {
|
||||
id: root
|
||||
default property var menuData
|
||||
property var model: [
|
||||
{ iconName: "start-here", text: "Start", action: () => {print("hello")} },
|
||||
{ type : "separator" },
|
||||
]
|
||||
readonly property bool hasIcons: model.some(item => item.iconName !== undefined && item.iconName !== "")
|
||||
padding: 2
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: root.model
|
||||
delegate: DelegateChooser {
|
||||
role: "type"
|
||||
DelegateChoice {
|
||||
roleValue: "separator"
|
||||
Rectangle {
|
||||
Layout.topMargin: 2
|
||||
Layout.bottomMargin: 2
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 1
|
||||
color: Looks.colors.bg0Border
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: undefined
|
||||
WButton {
|
||||
id: btn
|
||||
Layout.fillWidth: true
|
||||
inset: 2
|
||||
|
||||
required property var modelData
|
||||
forceShowIcon: root.hasIcons
|
||||
icon.name: modelData.iconName ? modelData.iconName : ""
|
||||
monochromeIcon: modelData.monochromeIcon ?? true
|
||||
text: modelData.text ? modelData.text : ""
|
||||
|
||||
onClicked: {
|
||||
if (modelData.action) modelData.action();
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
131
surfaces/quickshell/ii-base/modules/waffle/bar/BarPopup.qml
Normal file
131
surfaces/quickshell/ii-base/modules/waffle/bar/BarPopup.qml
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Loader {
|
||||
id: root
|
||||
|
||||
required property var contentItem
|
||||
property real padding: Looks.radius.large - Looks.radius.medium
|
||||
property bool noSmoothClosing: !Config.options.waffles.tweaks.smootherMenuAnimations
|
||||
property bool closeOnFocusLost: true
|
||||
signal focusCleared()
|
||||
|
||||
property Item anchorItem: parent
|
||||
property real visualMargin: 12
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
property real ambientShadowWidth: 1
|
||||
|
||||
onFocusCleared: {
|
||||
if (!root.closeOnFocusLost) return;
|
||||
root.close()
|
||||
}
|
||||
|
||||
function grabFocus() { // Doesn't work
|
||||
item.grabFocus();
|
||||
}
|
||||
|
||||
function close() {
|
||||
item.close();
|
||||
}
|
||||
|
||||
function updateAnchor() {
|
||||
item?.anchor.updateAnchor();
|
||||
}
|
||||
|
||||
active: false
|
||||
visible: active
|
||||
sourceComponent: PopupWindow {
|
||||
id: popupWindow
|
||||
visible: true
|
||||
Component.onCompleted: {
|
||||
openAnim.start();
|
||||
}
|
||||
|
||||
anchor {
|
||||
adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX
|
||||
item: root.anchorItem
|
||||
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
active: true
|
||||
windows: [popupWindow]
|
||||
onCleared: root.focusCleared();
|
||||
}
|
||||
|
||||
function close() {
|
||||
if (root.noSmoothClosing) root.active = false;
|
||||
else closeAnim.start();
|
||||
}
|
||||
|
||||
function grabFocus() {
|
||||
focusGrab.active = true; // Doesn't work
|
||||
}
|
||||
|
||||
implicitWidth: realContent.implicitWidth + (root.ambientShadowWidth * 2) + (root.visualMargin * 2)
|
||||
implicitHeight: realContent.implicitHeight + (root.ambientShadowWidth * 2) + (root.visualMargin * 2)
|
||||
|
||||
property real sourceEdgeMargin: -implicitHeight
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: (root.ambientShadowWidth + root.visualMargin)
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
PropertyAnimation {
|
||||
target: popupWindow
|
||||
property: "sourceEdgeMargin"
|
||||
to: -implicitHeight
|
||||
duration: 150
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
WAmbientShadow {
|
||||
target: realContent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.barAtBottom ? undefined : parent.top
|
||||
bottom: root.barAtBottom ? parent.bottom : undefined
|
||||
margins: root.ambientShadowWidth + root.visualMargin
|
||||
// Opening anim
|
||||
bottomMargin: root.barAtBottom ? popupWindow.sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin)
|
||||
topMargin: root.barAtBottom ? (root.ambientShadowWidth + root.visualMargin) : popupWindow.sourceEdgeMargin
|
||||
}
|
||||
color: Looks.colors.bg1Base
|
||||
radius: Looks.radius.large
|
||||
|
||||
// test
|
||||
implicitWidth: root.contentItem.implicitWidth + (root.padding * 2)
|
||||
implicitHeight: root.contentItem.implicitHeight + (root.padding * 2)
|
||||
|
||||
children: [root.contentItem]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WPopupToolTip {
|
||||
anchorEdges: Config.options.waffles.bar.bottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
iconName: checked ? "system-search-checked" : "system-search"
|
||||
separateLightDark: true
|
||||
|
||||
checked: GlobalStates.searchOpen && LauncherSearch.query !== ""
|
||||
onClicked: {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen; // For now...
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
id: tooltip
|
||||
text: Translation.tr("Search")
|
||||
extraVisibleCondition: root.shouldShowTooltip
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
|
||||
iconName: down ? "start-here-pressed" : "start-here"
|
||||
|
||||
checked: GlobalStates.searchOpen && LauncherSearch.query === ""
|
||||
onClicked: {
|
||||
GlobalStates.searchOpen = !GlobalStates.searchOpen;
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
id: tooltip
|
||||
text: Translation.tr("Start")
|
||||
extraVisibleCondition: root.shouldShowTooltip
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
contextMenu.active = true;
|
||||
}
|
||||
|
||||
BarMenu {
|
||||
id: contextMenu
|
||||
|
||||
model: [
|
||||
{
|
||||
text: Translation.tr("Terminal"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Task Manager"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.taskManager]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Settings"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("File Explorer"),
|
||||
action: () => {
|
||||
Qt.openUrlExternally(Directories.home);
|
||||
}
|
||||
},
|
||||
{
|
||||
text: Translation.tr("Search"),
|
||||
action: () => {
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "overview", "toggle"]);
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
107
surfaces/quickshell/ii-base/modules/waffle/bar/SystemButton.qml
Normal file
107
surfaces/quickshell/ii-base/modules/waffle/bar/SystemButton.qml
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BarButton {
|
||||
id: root
|
||||
|
||||
checked: GlobalStates.sidebarLeftOpen
|
||||
onClicked: {
|
||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: column.implicitHeight
|
||||
implicitWidth: column.implicitWidth
|
||||
Row {
|
||||
id: column
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
spacing: 4
|
||||
|
||||
IconHoverArea {
|
||||
id: internetHoverArea
|
||||
iconItem: FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "wifi-1"
|
||||
color: Looks.colors.inactiveIcon
|
||||
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: WIcons.internetIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IconHoverArea {
|
||||
id: volumeHoverArea
|
||||
iconItem: FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "speaker"
|
||||
color: Looks.colors.inactiveIcon
|
||||
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: WIcons.volumeIcon
|
||||
}
|
||||
}
|
||||
onScrollDown: Audio.decrementVolume();
|
||||
onScrollUp: Audio.incrementVolume();
|
||||
}
|
||||
|
||||
IconHoverArea {
|
||||
id: batteryHoverArea
|
||||
visible: Battery?.available ?? false
|
||||
iconItem: FluentIcon {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: WIcons.batteryLevelIcon
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: WIcons.batteryIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IconHoverArea: FocusedScrollMouseArea {
|
||||
id: hoverArea
|
||||
required property var iconItem
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
hoverEnabled: true
|
||||
implicitHeight: hoverArea.iconItem.implicitHeight
|
||||
implicitWidth: hoverArea.iconItem.implicitWidth
|
||||
|
||||
onPressed: (event) => event.accepted = false; // Don't consume clicks
|
||||
|
||||
children: [iconItem]
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip && internetHoverArea.containsMouse
|
||||
text: Translation.tr("%1\nInternet access").arg(Network.ethernet ? Translation.tr("Network") : Network.networkName)
|
||||
}
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip && volumeHoverArea.containsMouse
|
||||
text: Translation.tr("Speakers (%1): %2") //
|
||||
.arg(Audio.sink?.nickname || Audio.sink?.description || Translation.tr("Unknown")) //
|
||||
.arg(Audio.sink?.audio.muted ? Translation.tr("Muted") : `${Math.round(Audio.sink?.audio.volume * 100) || 0}%`) //
|
||||
}
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip && batteryHoverArea.containsMouse
|
||||
text: Translation.tr("Battery: %1%2") //
|
||||
.arg(`${Math.round(Battery.percentage * 100) || 0}%`) //
|
||||
.arg(Battery.isPluggedIn ? (" " + Translation.tr("(Plugged in)")) : "")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
iconName: (down && !checked) ? "task-view-pressed" : "task-view"
|
||||
pressedScale: checked ? 5/6 : 1
|
||||
separateLightDark: true
|
||||
|
||||
checked: GlobalStates.overviewOpen
|
||||
onClicked: {
|
||||
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip
|
||||
text: Translation.tr("Task View") // Should be a preview of workspaces, but we'll have this for now...
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BarButton {
|
||||
id: root
|
||||
|
||||
rightInset: 12 // For now this is the rightmost button. Desktop peek is useless. (for now)
|
||||
leftPadding: 12
|
||||
rightPadding: 22
|
||||
|
||||
checked: GlobalStates.sidebarRightOpen
|
||||
onClicked: {
|
||||
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen;
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
// anchors.centerIn: parent
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
Row {
|
||||
id: contentLayout
|
||||
anchors.centerIn: parent
|
||||
spacing: 7
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
WText {
|
||||
anchors.right: parent.right
|
||||
text: DateTime.time
|
||||
}
|
||||
WText {
|
||||
anchors.right: parent.right
|
||||
text: DateTime.date
|
||||
}
|
||||
}
|
||||
FluentIcon {
|
||||
visible: Notifications.silent
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
icon: "alert-snooze"
|
||||
implicitSize: 18
|
||||
filled: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
id: tooltip
|
||||
extraVisibleCondition: root.shouldShowTooltip
|
||||
text: `${Qt.locale().toString(DateTime.clock.date, "dddd, MMMM d, yyyy")}\n\n${Qt.locale().toString(DateTime.clock.date, "ddd hh:mm AP")}`
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar.tray
|
||||
|
||||
BarIconButton {
|
||||
id: root
|
||||
|
||||
visible: Updates.updateAdvised || Updates.updateStronglyAdvised
|
||||
padding: 4
|
||||
iconName: "arrow-sync"
|
||||
iconSize: 20 // Needed because the icon appears to have some padding
|
||||
iconMonochrome: true
|
||||
tooltipText: Translation.tr("Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages").arg(Updates.count)
|
||||
|
||||
onClicked: {
|
||||
Quickshell.execDetached(["bash", "-c", Config.options.apps.update]);
|
||||
}
|
||||
|
||||
overlayingItems: Rectangle {
|
||||
anchors {
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
margins: 1
|
||||
}
|
||||
implicitWidth: 8
|
||||
implicitHeight: implicitWidth
|
||||
radius: height / 2
|
||||
color: Updates.updateStronglyAdvised ? Looks.colors.warning : Looks.colors.accent
|
||||
}
|
||||
}
|
||||
88
surfaces/quickshell/ii-base/modules/waffle/bar/WaffleBar.qml
Normal file
88
surfaces/quickshell/ii-base/modules/waffle/bar/WaffleBar.qml
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Scope {
|
||||
id: root
|
||||
|
||||
LazyLoader {
|
||||
id: barLoader
|
||||
active: GlobalStates.barOpen
|
||||
component: Variants {
|
||||
model: Quickshell.screens
|
||||
delegate: PanelWindow { // Bar window
|
||||
id: barRoot
|
||||
required property var modelData
|
||||
screen: modelData
|
||||
exclusionMode: ExclusionMode.Ignore
|
||||
exclusiveZone: implicitHeight
|
||||
WlrLayershell.namespace: "quickshell:bar"
|
||||
|
||||
anchors {
|
||||
left: true
|
||||
right: true
|
||||
bottom: Config.options.waffles.bar.bottom
|
||||
top: !Config.options.waffles.bar.bottom
|
||||
}
|
||||
|
||||
color: "transparent"
|
||||
implicitHeight: content.implicitHeight
|
||||
implicitWidth: content.implicitWidth
|
||||
|
||||
WaffleBarContent {
|
||||
id: content
|
||||
anchors.fill: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "bar"
|
||||
|
||||
function toggle(): void {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen
|
||||
}
|
||||
|
||||
function close(): void {
|
||||
GlobalStates.barOpen = false
|
||||
}
|
||||
|
||||
function open(): void {
|
||||
GlobalStates.barOpen = true
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barToggle"
|
||||
description: "Toggles bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = !GlobalStates.barOpen;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barOpen"
|
||||
description: "Opens bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalShortcut {
|
||||
name: "barClose"
|
||||
description: "Closes bar on press"
|
||||
|
||||
onPressed: {
|
||||
GlobalStates.barOpen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar.tasks
|
||||
import qs.modules.waffle.bar.tray
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
color: Looks.colors.bg0
|
||||
implicitHeight: 48
|
||||
|
||||
Rectangle {
|
||||
id: border
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: Config.options.waffles.bar.bottom ? parent.top : undefined
|
||||
bottom: Config.options.waffles.bar.bottom ? undefined : parent.bottom
|
||||
}
|
||||
color: Looks.colors.bg0Border
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
||||
BarGroupRow {
|
||||
id: bloatRow
|
||||
anchors.left: parent.left
|
||||
opacity: Config.options.waffles.bar.leftAlignApps ? 0 : 1
|
||||
visible: opacity > 0
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
|
||||
WidgetsButton {}
|
||||
}
|
||||
|
||||
BarGroupRow {
|
||||
id: appsRow
|
||||
anchors.left: undefined
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
states: State {
|
||||
name: "left"
|
||||
when: Config.options.waffles.bar.leftAlignApps
|
||||
AnchorChanges {
|
||||
target: appsRow
|
||||
anchors.left: parent.left
|
||||
anchors.horizontalCenter: undefined
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
animations: Looks.transition.anchor.createObject(this)
|
||||
}
|
||||
|
||||
StartButton {}
|
||||
SearchButton {}
|
||||
TaskViewButton {}
|
||||
Tasks {}
|
||||
}
|
||||
|
||||
BarGroupRow {
|
||||
id: systemRow
|
||||
anchors.right: parent.right
|
||||
FadeLoader {
|
||||
Layout.fillHeight: true
|
||||
shown: Config.options.waffles.bar.leftAlignApps
|
||||
sourceComponent: WidgetsButton {}
|
||||
}
|
||||
Tray {}
|
||||
UpdatesButton {}
|
||||
SystemButton {}
|
||||
TimeButton {}
|
||||
}
|
||||
|
||||
component BarGroupRow: RowLayout {
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
spacing: 0
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
readonly property bool expandedForm: Config.options.waffles.bar.leftAlignApps
|
||||
leftInset: Config.options.waffles.bar.leftAlignApps ? 0 : 12
|
||||
implicitWidth: expandedForm ? 148 : (height - topInset - bottomInset + leftInset + rightInset)
|
||||
iconName: "widgets"
|
||||
|
||||
checked: GlobalStates.sidebarLeftOpen
|
||||
onClicked: {
|
||||
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen
|
||||
}
|
||||
onDownChanged: {
|
||||
scaleAnim.duration = root.down ? 150 : 200
|
||||
scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut
|
||||
iconWidget.scale = root.down ? 5/6 : 1 // If/When we do dragging, the scale is 1.25
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: root.expandedForm ? parent.left : undefined
|
||||
horizontalCenter: root.expandedForm ? undefined : background.horizontalCenter
|
||||
}
|
||||
implicitHeight: row.implicitHeight
|
||||
implicitWidth: row.implicitWidth
|
||||
Row {
|
||||
id: row
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: root.expandedForm ? parent.left : undefined
|
||||
horizontalCenter: root.expandedForm ? undefined : parent.horizontalCenter
|
||||
margins: 8
|
||||
}
|
||||
spacing: 6
|
||||
|
||||
WAppIcon {
|
||||
id: iconWidget
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
iconName: root.iconName
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
id: scaleAnim
|
||||
easing.type: Easing.BezierSpline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
visible: root.expandedForm
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
WText {
|
||||
text: Translation.tr("Widgets")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip
|
||||
text: Translation.tr("Widgets")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
import Quickshell
|
||||
|
||||
AppButton {
|
||||
id: root
|
||||
|
||||
required property var appEntry
|
||||
readonly property bool isSeparator: appEntry.appId === "SEPARATOR"
|
||||
property var desktopEntry: DesktopEntries.heuristicLookup(appEntry.appId)
|
||||
|
||||
Timer {
|
||||
// Retry looking up the desktop entry if it failed (e.g. database not loaded yet)
|
||||
property int retryCount: 5
|
||||
interval: 1000
|
||||
running: !root.isSeparator && root.desktopEntry === null && retryCount > 0
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
retryCount--;
|
||||
root.desktopEntry = DesktopEntries.heuristicLookup(root.appEntry.appId);
|
||||
}
|
||||
}
|
||||
|
||||
property bool active: root.appEntry.toplevels.some(t => t.activated)
|
||||
property bool hasWindows: appEntry.toplevels.length > 0
|
||||
|
||||
signal hoverPreviewRequested()
|
||||
signal hoverPreviewDismissed()
|
||||
|
||||
multiple: appEntry.toplevels.length > 1
|
||||
checked: active
|
||||
iconName: AppSearch.guessIcon(appEntry.appId)
|
||||
tryCustomIcon: false
|
||||
|
||||
onHoverTimedOut: {
|
||||
root.hoverPreviewRequested()
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.hoverTimer.stop() // Prevents preview showing up when clicking to focus
|
||||
if (root.multiple) {
|
||||
root.hoverPreviewRequested()
|
||||
} else if (root.appEntry.toplevels.length === 1) {
|
||||
root.appEntry.toplevels[0].activate()
|
||||
} else {
|
||||
root.desktopEntry.execute()
|
||||
}
|
||||
}
|
||||
|
||||
middleClickAction: () => {
|
||||
if (root.desktopEntry) {
|
||||
desktopEntry.execute()
|
||||
}
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
root.hoverPreviewDismissed()
|
||||
root.hoverTimer.stop()
|
||||
contextMenu.active = true;
|
||||
}
|
||||
|
||||
// Active indicator
|
||||
Rectangle {
|
||||
id: activeIndicator
|
||||
opacity: root.hasWindows ? 1 : 0
|
||||
anchors {
|
||||
horizontalCenter: root.background.horizontalCenter
|
||||
bottom: root.background.bottom
|
||||
bottomMargin: 1
|
||||
}
|
||||
|
||||
implicitWidth: root.active ? 16 : 6
|
||||
implicitHeight: 3
|
||||
radius: height / 2
|
||||
|
||||
color: root.active ? Looks.colors.accent : Looks.colors.accentUnfocused
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip && !root.hasWindows
|
||||
text: desktopEntry ? desktopEntry.name : appEntry.appId
|
||||
}
|
||||
|
||||
BarMenu {
|
||||
id: contextMenu
|
||||
noSmoothClosing: false // On the real thing this is always smooth
|
||||
|
||||
model: [
|
||||
...((root.desktopEntry?.actions.length > 0) ? root.desktopEntry.actions.map(action =>({
|
||||
iconName: action.icon,
|
||||
text: action.name,
|
||||
action: () => {
|
||||
action.execute()
|
||||
}
|
||||
})).concat({ type: "separator" }) : []),
|
||||
{
|
||||
iconName: root.iconName,
|
||||
text: root.desktopEntry ? root.desktopEntry.name : StringUtils.toTitleCase(appEntry.appId),
|
||||
monochromeIcon: false,
|
||||
action: () => {
|
||||
if (root.desktopEntry) {
|
||||
root.desktopEntry.execute()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
iconName: root.appEntry.pinned ? "pin-off" : "pin",
|
||||
text: root.appEntry.pinned ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar"),
|
||||
action: () => {
|
||||
TaskbarApps.togglePin(root.appEntry.appId);
|
||||
}
|
||||
},
|
||||
...(root.appEntry.toplevels.length > 0 ? [{
|
||||
iconName: "dismiss",
|
||||
text: root.multiple ? Translation.tr("Close all windows") : Translation.tr("Close window"),
|
||||
action: () => {
|
||||
for (let toplevel of root.appEntry.toplevels) {
|
||||
toplevel.close();
|
||||
}
|
||||
}
|
||||
}] : []),
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import Quickshell
|
||||
|
||||
PopupWindow {
|
||||
id: root
|
||||
|
||||
///////////////////// Properties ////////////////////
|
||||
required property bool tasksHovered
|
||||
property var appEntry
|
||||
property Item anchorItem
|
||||
|
||||
//////////////////// Functions ////////////////////
|
||||
function close() { // Closing doesn't animate, not sure if they're just lazy or it's intentional
|
||||
marginBehavior.enabled = false;
|
||||
root.visible = false;
|
||||
}
|
||||
|
||||
function open() {
|
||||
marginBehavior.enabled = true;
|
||||
root.visible = true;
|
||||
}
|
||||
|
||||
function show(appEntry: var, button: Item) {
|
||||
root.appEntry = appEntry;
|
||||
root.anchorItem = button;
|
||||
root.anchor.updateAnchor();
|
||||
root.open();
|
||||
}
|
||||
|
||||
///////////////////// Internals /////////////////////
|
||||
readonly property bool bottom: Config.options.waffles.bar.bottom
|
||||
property real visualMargin: 12
|
||||
property real ambientShadowWidth: 1
|
||||
|
||||
visible: false
|
||||
color: "transparent"
|
||||
implicitWidth: contentItem.implicitWidth + ambientShadowWidth + (visualMargin * 2)
|
||||
implicitHeight: contentItem.implicitHeight + ambientShadowWidth + (visualMargin * 2)
|
||||
anchor {
|
||||
adjustment: PopupAdjustment.Slide
|
||||
item: root.anchorItem
|
||||
gravity: bottom ? Edges.Top : Edges.Bottom
|
||||
edges: bottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 250
|
||||
running: root.visible && !hoverChecker.containsMouse && !root.tasksHovered
|
||||
onTriggered: {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
// Content
|
||||
MouseArea {
|
||||
id: hoverChecker
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
|
||||
// Shadow
|
||||
WAmbientShadow {
|
||||
target: contentItem
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentItem
|
||||
property real sourceEdgeMargin: root.visible ? (root.ambientShadowWidth + root.visualMargin) : -root.implicitHeight
|
||||
Behavior on sourceEdgeMargin {
|
||||
id: marginBehavior
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.bottom ? undefined : parent.top
|
||||
bottom: root.bottom ? parent.bottom : undefined
|
||||
margins: root.ambientShadowWidth + root.visualMargin
|
||||
// Opening anim
|
||||
bottomMargin: root.bottom ? sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin)
|
||||
topMargin: root.bottom ? (root.ambientShadowWidth + root.visualMargin) : sourceEdgeMargin
|
||||
}
|
||||
color: Looks.colors.bg1Base
|
||||
radius: Looks.radius.large
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: contentItem.width
|
||||
height: contentItem.height
|
||||
radius: contentItem.radius
|
||||
}
|
||||
}
|
||||
|
||||
// Testing
|
||||
implicitHeight: Math.min(158, windowsRow.implicitHeight)
|
||||
implicitWidth: windowsRow.implicitWidth
|
||||
|
||||
RowLayout {
|
||||
id: windowsRow
|
||||
anchors.fill: parent
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: root.appEntry?.toplevels ?? []
|
||||
}
|
||||
delegate: WindowPreview {
|
||||
required property var modelData
|
||||
toplevel: modelData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: appRow.implicitHeight
|
||||
implicitWidth: appRow.implicitWidth
|
||||
hoverEnabled: true
|
||||
|
||||
function showPreviewPopup(appEntry, button) {
|
||||
previewPopup.show(appEntry, button);
|
||||
}
|
||||
|
||||
Behavior on implicitWidth {
|
||||
animation: Looks.transition.move.createObject(this)
|
||||
}
|
||||
|
||||
WListView {
|
||||
id: appRow
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
orientation: Qt.Horizontal
|
||||
spacing: 0
|
||||
implicitWidth: contentWidth
|
||||
clip: true
|
||||
interactive: false
|
||||
// TODO: Include only apps (and windows) in current workspace only | wait, does that even make sense in a Hyprland workflow?
|
||||
model: ScriptModel {
|
||||
objectProp: "appId"
|
||||
values: TaskbarApps.apps.filter(app => app.appId !== "SEPARATOR")
|
||||
}
|
||||
delegate: TaskAppButton {
|
||||
required property var modelData
|
||||
appEntry: modelData
|
||||
|
||||
onHoverPreviewRequested: {
|
||||
root.showPreviewPopup(appEntry, this);
|
||||
}
|
||||
onHoverPreviewDismissed: {
|
||||
previewPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Previews popup
|
||||
TaskPreview {
|
||||
id: previewPopup
|
||||
tasksHovered: root.containsMouse
|
||||
anchor.window: root.QsWindow.window
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
required property var toplevel
|
||||
property real previewWidthConstraint: 200
|
||||
property real previewHeightConstraint: 110
|
||||
padding: 5
|
||||
Layout.fillHeight: true
|
||||
|
||||
onClicked: {
|
||||
root.toplevel.activate(); // TODO: make this work with those who disable focus on activate because telegram is abusive
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: background
|
||||
radius: Looks.radius.medium
|
||||
color: root.down ? Looks.colors.bg2Active : (root.hovered ? Looks.colors.bg2Hover : ColorUtils.transparentize(Looks.colors.bg2))
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: contentItem
|
||||
anchors.fill: parent
|
||||
anchors.margins: root.padding
|
||||
spacing: 5
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: false
|
||||
spacing: 8
|
||||
|
||||
WAppIcon {
|
||||
id: appIcon
|
||||
Layout.leftMargin: Looks.radius.large - root.padding + 2
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
iconName: AppSearch.guessIcon(root.toplevel.appId)
|
||||
implicitSize: 16
|
||||
}
|
||||
|
||||
Item {
|
||||
id: appTitleContainer
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitHeight: closeButton.implicitHeight // Enforce height, because closeButton doesn't contribute when it's invisible
|
||||
WText {
|
||||
id: appTitleText
|
||||
anchors.fill: parent
|
||||
text: root.toplevel.title
|
||||
elide: Text.ElideRight
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.thin
|
||||
color: Looks.colors.fg1
|
||||
}
|
||||
}
|
||||
|
||||
WindowCloseButton {
|
||||
id: closeButton
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: Looks.radius.large - root.padding
|
||||
Layout.topMargin: 0
|
||||
implicitWidth: Math.max(screencopyView.implicitWidth, 80)
|
||||
implicitHeight: screencopyView.implicitHeight
|
||||
|
||||
ScreencopyView {
|
||||
id: screencopyView
|
||||
anchors.centerIn: parent
|
||||
captureSource: root.toplevel
|
||||
live: true
|
||||
paintCursor: true
|
||||
constraintSize: Qt.size(root.previewWidthConstraint, root.previewHeightConstraint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component WindowCloseButton: CloseButton {
|
||||
visible: root.hovered
|
||||
Layout.leftMargin: 4
|
||||
implicitHeight: 30
|
||||
implicitWidth: 30
|
||||
radius: Looks.radius.large - root.padding
|
||||
onClicked: {
|
||||
root.toplevel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
118
surfaces/quickshell/ii-base/modules/waffle/bar/tray/Tray.qml
Normal file
118
surfaces/quickshell/ii-base/modules/waffle/bar/tray/Tray.qml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt.labs.synchronizer
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
|
||||
property bool overflowOpen: false
|
||||
property bool dragging: false
|
||||
|
||||
Layout.fillHeight: true
|
||||
spacing: 0
|
||||
|
||||
BarIconButton {
|
||||
id: overflowButton
|
||||
|
||||
visible: (TrayService.unpinnedItems.length > 0 || root.dragging)
|
||||
checked: root.overflowOpen
|
||||
|
||||
iconName: "chevron-down"
|
||||
iconMonochrome: true
|
||||
iconRotation: (Config.options.waffles.bar.bottom ? 180 : 0) + (root.overflowOpen ? 180 : 0)
|
||||
Behavior on iconRotation {
|
||||
animation: Looks.transition.rotate.createObject(this)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
root.overflowOpen = !root.overflowOpen;
|
||||
}
|
||||
|
||||
TrayOverflowMenu {
|
||||
id: trayOverflowLayout
|
||||
Synchronizer on active {
|
||||
property alias source: root.overflowOpen
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: overflowButton.shouldShowTooltip
|
||||
text: Translation.tr("Show hidden icons")
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: pinDropArea
|
||||
anchors.fill: parent
|
||||
property bool willPin: false
|
||||
onEntered: willPin = true
|
||||
onExited: willPin = false
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: TrayService.pinnedItems
|
||||
}
|
||||
delegate: TrayButton {
|
||||
id: trayButton
|
||||
required property var modelData
|
||||
item: modelData
|
||||
|
||||
property real initialX
|
||||
property real initialY
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
drag.target: parent
|
||||
drag.axis: Drag.XAxis
|
||||
drag.threshold: 2
|
||||
|
||||
onPressed: event => {
|
||||
trayButton.Drag.hotSpot.x = event.x;
|
||||
trayButton.initialX = trayButton.x;
|
||||
root.dragging = true;
|
||||
trayButton.Drag.active = true;
|
||||
}
|
||||
onPositionChanged: {
|
||||
pinTooltip.updateAnchor();
|
||||
}
|
||||
onReleased: {
|
||||
if (!dragArea.drag.active) {
|
||||
trayButton.click();
|
||||
} else {
|
||||
if (pinDropArea.containsDrag && pinDropArea.willPin) {
|
||||
// Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out...
|
||||
trayButton.visible = false;
|
||||
TrayService.togglePin(trayButton.item.id);
|
||||
pinDropArea.willPin = false;
|
||||
} else {
|
||||
trayButton.x = trayButton.initialX;
|
||||
}
|
||||
}
|
||||
trayButton.Drag.active = false;
|
||||
root.dragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
id: pinTooltip
|
||||
extraVisibleCondition: trayButton.Drag.active && pinDropArea.containsDrag && pinDropArea.willPin
|
||||
horizontalPadding: 6
|
||||
verticalPadding: 6
|
||||
realContentItem: FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: "pin-off"
|
||||
implicitSize: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Services.SystemTray
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
|
||||
BarIconButton {
|
||||
id: root
|
||||
|
||||
required property SystemTrayItem item
|
||||
property alias menuOpen: menu.visible
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
iconSource: item.icon
|
||||
iconScale: 0
|
||||
Component.onCompleted: {
|
||||
root.iconScale = 1
|
||||
}
|
||||
Behavior on iconScale {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
item.activate();
|
||||
}
|
||||
|
||||
altAction: () => {
|
||||
if (item.hasMenu) menu.open()
|
||||
}
|
||||
|
||||
// This is lazy, but it's not like tray menus on Windoes are consistent...
|
||||
// TODO: Figure out how to do cascading menus then use a custom menu
|
||||
QsMenuAnchor {
|
||||
id: menu
|
||||
menu: root.item.menu
|
||||
anchor {
|
||||
adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX
|
||||
item: root
|
||||
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
|
||||
}
|
||||
}
|
||||
|
||||
BarToolTip {
|
||||
extraVisibleCondition: root.shouldShowTooltip && !root.Drag.active
|
||||
text: TrayService.getTooltipForItem(root.item)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
|
||||
BarPopup {
|
||||
id: root
|
||||
|
||||
closeOnFocusLost: false
|
||||
onFocusCleared: {
|
||||
const hasMenuOpen = contentItem.children.some(c => (c.menuOpen));
|
||||
if (!hasMenuOpen)
|
||||
root.close();
|
||||
else
|
||||
root.grabFocus();
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: contentGrid.implicitWidth
|
||||
implicitHeight: contentGrid.implicitHeight
|
||||
GridLayout {
|
||||
id: contentGrid
|
||||
anchors.centerIn: parent
|
||||
rows: Math.floor(Math.sqrt(TrayService.unpinnedItems.length))
|
||||
columns: Math.ceil(TrayService.unpinnedItems.length / rows)
|
||||
columnSpacing: 0
|
||||
rowSpacing: 0
|
||||
|
||||
Repeater {
|
||||
model: ScriptModel {
|
||||
values: TrayService.unpinnedItems
|
||||
onValuesChanged: {
|
||||
root.updateAnchor();
|
||||
if (values.length === 0) {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate: TrayButton {
|
||||
id: trayButton
|
||||
required property var modelData
|
||||
item: modelData
|
||||
|
||||
topInset: 0
|
||||
bottomInset: 0
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
colBackground: ColorUtils.transparentize(Looks.colors.bg2)
|
||||
colBackgroundHover: Looks.colors.bg2Hover
|
||||
colBackgroundActive: Looks.colors.bg2Active
|
||||
|
||||
onMenuOpenChanged: {
|
||||
// The overflow menu should only be closed when the user clicks outside
|
||||
// However the focus grab refuses to reactivate, so we can't have that
|
||||
// But most of the time the user dismisses the menu by clicking outside anyway,
|
||||
// so this is acceptable.
|
||||
if (!menuOpen) {
|
||||
root.close();
|
||||
}
|
||||
}
|
||||
|
||||
property real initialX
|
||||
property real initialY
|
||||
|
||||
Behavior on x {
|
||||
animation: Looks.transition.move.createObject(this)
|
||||
}
|
||||
Behavior on y {
|
||||
animation: Looks.transition.move.createObject(this)
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: dragArea
|
||||
anchors.fill: parent
|
||||
drag.target: parent
|
||||
drag.threshold: 2
|
||||
|
||||
onPressed: event => {
|
||||
trayButton.Drag.hotSpot.x = event.x;
|
||||
trayButton.Drag.hotSpot.y = event.y;
|
||||
trayButton.initialX = trayButton.x;
|
||||
trayButton.initialY = trayButton.y;
|
||||
trayButton.Drag.active = true;
|
||||
}
|
||||
onReleased: {
|
||||
if (!dragArea.drag.active) {
|
||||
trayButton.click();
|
||||
} else {
|
||||
if (!unpinDropArea.containsDrag && unpinDropArea.willUnpin) {
|
||||
// Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out...
|
||||
trayButton.visible = false;
|
||||
TrayService.togglePin(trayButton.item.id);
|
||||
unpinDropArea.willUnpin = false;
|
||||
} else {
|
||||
trayButton.x = trayButton.initialX;
|
||||
trayButton.y = trayButton.initialY;
|
||||
}
|
||||
}
|
||||
trayButton.Drag.active = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DropArea {
|
||||
id: unpinDropArea
|
||||
anchors.fill: parent
|
||||
property bool willUnpin: false
|
||||
onEntered: willUnpin = false
|
||||
onExited: willUnpin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
381
surfaces/quickshell/ii-base/modules/waffle/lock/WaffleLock.qml
Normal file
381
surfaces/quickshell/ii-base/modules/waffle/lock/WaffleLock.qml
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Hyprland
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.panels.lock
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.sessionScreen as SessionScreen
|
||||
|
||||
LockScreen {
|
||||
id: root
|
||||
|
||||
property bool passwordView: false
|
||||
|
||||
lockSurface: Item {
|
||||
id: lockSurfaceItem
|
||||
|
||||
Component.onCompleted: {
|
||||
root.passwordView = false;
|
||||
lockSurfaceItem.forceActiveFocus();
|
||||
}
|
||||
|
||||
Keys.onPressed: {
|
||||
interactables.switchToFocusedView();
|
||||
}
|
||||
|
||||
StyledImage {
|
||||
id: bg
|
||||
z: 0
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready) {
|
||||
print("Lock wallpaper loaded");
|
||||
print(lockSurfaceItem.height);
|
||||
y = -lockSurfaceItem.height;
|
||||
openAnim.restart();
|
||||
}
|
||||
}
|
||||
source: Config.options.background.wallpaperPath
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
PropertyAnimation {
|
||||
id: openAnim
|
||||
target: bg
|
||||
property: "y"
|
||||
to: 0
|
||||
duration: 350
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
GaussianBlur {
|
||||
z: 1
|
||||
anchors.fill: bg
|
||||
source: bg
|
||||
radius: 100
|
||||
samples: radius * 2 + 1
|
||||
scale: root.passwordView ? 1.1 : 1
|
||||
opacity: root.passwordView ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
|
||||
Behavior on scale {
|
||||
NumberAnimation {
|
||||
duration: 400
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Interactables {
|
||||
id: interactables
|
||||
z: 2
|
||||
anchors.fill: bg
|
||||
}
|
||||
}
|
||||
|
||||
component Interactables: Rectangle {
|
||||
id: interactablesComponent
|
||||
color: ColorUtils.transparentize("#000000", 0.8)
|
||||
// Button {
|
||||
// onClicked: {
|
||||
// root.context.unlocked(LockContext.ActionEnum.Unlock);
|
||||
// GlobalStates.screenLocked = false;
|
||||
// }
|
||||
// text: "woah it doesnt work let me out pls uwu colon three"
|
||||
// }
|
||||
|
||||
function switchToFocusedView() {
|
||||
switchToPasswordViewAnim.restart();
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: switchToPasswordViewAnim
|
||||
PropertyAnimation {
|
||||
target: unfocusedContent
|
||||
property: "y"
|
||||
from: 0
|
||||
to: -height * 1.1
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.passwordView = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: unfocusedContent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
visible: !root.passwordView
|
||||
ClockTextGroup {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
topMargin: interactablesComponent.height * 0.1
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: 21
|
||||
rightMargin: 31
|
||||
}
|
||||
IconIndicator {
|
||||
baseIcon: "wifi-1"
|
||||
icon: WIcons.internetIcon
|
||||
}
|
||||
IconIndicator {
|
||||
baseIcon: WIcons.batteryIcon
|
||||
icon: WIcons.batteryLevelIcon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: focusedContent
|
||||
anchors.fill: parent
|
||||
visible: root.passwordView
|
||||
|
||||
PasswordGroup {
|
||||
visible: root.passwordView
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: root.passwordView
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
right: parent.right
|
||||
bottomMargin: 21
|
||||
rightMargin: 31
|
||||
}
|
||||
SessionScreen.PowerButton {
|
||||
id: powerButton
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component IconIndicator: Item {
|
||||
id: iconIndicator
|
||||
required property string baseIcon
|
||||
required property string icon
|
||||
default property alias indicatorData: iconWidget.data
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
FluentIcon {
|
||||
id: iconWidget
|
||||
anchors.centerIn: parent
|
||||
icon: iconIndicator.baseIcon
|
||||
color: Looks.darkColors.inactiveIcon
|
||||
implicitSize: 20
|
||||
FluentIcon {
|
||||
anchors.fill: parent
|
||||
icon: iconIndicator.icon
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component ClockTextGroup: Column {
|
||||
id: clockTextGroup
|
||||
spacing: -3
|
||||
|
||||
WText {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
color: Looks.darkColors.fg
|
||||
font.pixelSize: 133
|
||||
font.weight: Looks.font.weight.strong
|
||||
text: {
|
||||
// Don't take am/pm
|
||||
// Match groups of digits separated by non-digit chars (e.g., "12:34", "12.34", "12-34")
|
||||
let match = DateTime.time.match(/(\d{1,2})\D+(\d{2})/);
|
||||
return match ? `${match[1]}${DateTime.time.match(/\D+/)[0]}${match[2]}` : DateTime.time;
|
||||
}
|
||||
}
|
||||
|
||||
WText {
|
||||
id: dateLabel
|
||||
color: Looks.darkColors.fg
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.pixelSize: 28
|
||||
font.weight: Looks.font.weight.strong
|
||||
text: DateTime.collapsedCalendarFormat
|
||||
}
|
||||
}
|
||||
|
||||
component PasswordGroup: ColumnLayout {
|
||||
id: passwordGroup
|
||||
spacing: 15
|
||||
|
||||
WUserAvatar {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
sourceSize: Qt.size(192, 192)
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: SystemInfo.username
|
||||
color: Looks.darkColors.fg
|
||||
font.pixelSize: 26
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: passwordInputWrapper
|
||||
Layout.topMargin: 10
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.bottomMargin: 132
|
||||
color: "transparent"
|
||||
implicitWidth: 296
|
||||
implicitHeight: 36
|
||||
border.width: 2
|
||||
border.color: Looks.applyContentTransparency(Looks.darkColors.bg1Border)
|
||||
radius: Looks.radius.medium
|
||||
|
||||
Rectangle {
|
||||
id: passwordInputBackground
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
radius: Looks.radius.small + 1
|
||||
color: passwordInput.focus ? Looks.applyBackgroundTransparency(Looks.darkColors.bg1Base) : Looks.applyContentTransparency(Looks.darkColors.bg1)
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 6
|
||||
spacing: 3
|
||||
|
||||
WTextInput {
|
||||
id: passwordInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
verticalAlignment: TextInput.AlignVCenter
|
||||
inputMethodHints: Qt.ImhSensitiveData
|
||||
echoMode: passwordVisibilityButton.pressed ? TextInput.Normal : TextInput.Password
|
||||
color: Looks.darkColors.fg
|
||||
|
||||
font.pixelSize: 12
|
||||
WText {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: passwordInput.text.length === 0
|
||||
text: Translation.tr("Password")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: Looks.darkColors.fg
|
||||
opacity: 0.8
|
||||
}
|
||||
|
||||
onTextChanged: root.context.currentText = this.text
|
||||
onAccepted: {
|
||||
root.context.tryUnlock();
|
||||
}
|
||||
Connections {
|
||||
target: root.context
|
||||
function onCurrentTextChanged() {
|
||||
passwordInput.text = root.context.currentText;
|
||||
}
|
||||
}
|
||||
Connections {
|
||||
target: root
|
||||
function onPasswordViewChanged() {
|
||||
passwordInput.forceActiveFocus();
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onPressed: event => {
|
||||
root.context.resetClearTimer();
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
cursorShape: Qt.IBeamCursor
|
||||
}
|
||||
}
|
||||
|
||||
PasswordBoxButton {
|
||||
id: passwordVisibilityButton
|
||||
property bool passwordVisible: false
|
||||
visible: passwordInput.text.length > 0
|
||||
onPressed: passwordVisible = true
|
||||
onReleased: passwordVisible = false
|
||||
icon.name: passwordVisible ? "eye-off" : "eye"
|
||||
}
|
||||
|
||||
PasswordBoxButton {
|
||||
onClicked: {
|
||||
root.context.tryUnlock();
|
||||
}
|
||||
icon.name: "arrow-right"
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
id: activeIndicatorLine
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
bottom: parent.bottom
|
||||
}
|
||||
implicitHeight: 2
|
||||
color: passwordInput.focus ? Looks.colors.accent : Looks.applyContentTransparency(Looks.darkColors.bg2Border)
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: passwordInputWrapper.width
|
||||
height: passwordInputWrapper.height
|
||||
radius: passwordInputWrapper.radius
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {}
|
||||
}
|
||||
|
||||
component PasswordBoxButton: WButton {
|
||||
id: pwBoxBtn
|
||||
implicitWidth: 28
|
||||
implicitHeight: 22
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.darkColors.bg1)
|
||||
property color colBackgroundHover: ColorUtils.transparentize(Looks.darkColors.bg2Hover)
|
||||
property color colBackgroundActive: ColorUtils.transparentize(Looks.darkColors.bg2Active)
|
||||
fgColor: checked ? Looks.colors.accentFg : Looks.darkColors.fg
|
||||
|
||||
checked: hovered
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
color: pwBoxBtn.fgColor
|
||||
anchors.centerIn: parent
|
||||
icon: pwBoxBtn.icon.name
|
||||
implicitSize: 16
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
colBackground: Looks.colors.bg1
|
||||
colBackgroundHover: Looks.colors.bg1Hover
|
||||
colBackgroundActive: Looks.colors.bg1Active
|
||||
property color colBackgroundBorder
|
||||
property color color
|
||||
property alias border: background.border
|
||||
property alias shinyColor: background.borderColor
|
||||
|
||||
colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0)
|
||||
color: {
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive
|
||||
} else if ((root.hovered && !root.down) || root.checked) {
|
||||
return root.colBackgroundHover
|
||||
} else {
|
||||
return root.colBackground
|
||||
}
|
||||
}
|
||||
|
||||
background: AcrylicRectangle {
|
||||
id: background
|
||||
shiny: ((root.hovered && !root.down) || root.checked)
|
||||
color: root.color
|
||||
radius: Looks.radius.medium
|
||||
border.width: 1
|
||||
border.color: root.colBackgroundBorder
|
||||
|
||||
Behavior on border.color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
property bool shiny: true // Top border
|
||||
property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7)
|
||||
property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1)
|
||||
color: Looks.colors.bg1Hover
|
||||
radius: Looks.radius.medium
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
Behavior on internalBorderColor {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
onInternalBorderColorChanged: {
|
||||
borderCanvas.requestPaint();
|
||||
}
|
||||
|
||||
// 1px border at the top or bottom
|
||||
Canvas {
|
||||
id: borderCanvas
|
||||
anchors.fill: parent
|
||||
// For dark mode we have a shiny top border, and for light mode we have sort of a shadow
|
||||
rotation: Looks.dark ? 0 : 180
|
||||
onPaint: {
|
||||
var ctx = getContext("2d");
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
|
||||
var borderColor = root.internalBorderColor;
|
||||
|
||||
var r = root.radius;
|
||||
var fadeLength = Math.max(1, r);
|
||||
var fadeLengthPercent = fadeLength / width;
|
||||
|
||||
// Compute normalized stops
|
||||
var leftFadeStop = fadeLengthPercent;
|
||||
var rightFadeStop = 1 - fadeLengthPercent;
|
||||
|
||||
var grad = ctx.createLinearGradient(0, 0, width, 0);
|
||||
grad.addColorStop(0, Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 0));
|
||||
grad.addColorStop(leftFadeStop, borderColor);
|
||||
grad.addColorStop(rightFadeStop, borderColor);
|
||||
grad.addColorStop(1, Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 0));
|
||||
|
||||
ctx.strokeStyle = grad;
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(r, 0.5);
|
||||
ctx.lineTo(width - r, 0.5);
|
||||
// Top-right curve
|
||||
ctx.arcTo(width, 0.5, width, r + 0.5, r);
|
||||
// Top-left curve
|
||||
ctx.moveTo(width - r, 0.5);
|
||||
ctx.arcTo(0, 0.5, 0, r + 0.5, r);
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelBody
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
import Quickshell
|
||||
|
||||
Button {
|
||||
id: reusableCloseButton
|
||||
implicitHeight: 30
|
||||
implicitWidth: 30
|
||||
property alias radius: closeButtonBg.radius
|
||||
|
||||
Rectangle {
|
||||
z: 0
|
||||
color: "transparent"
|
||||
anchors.fill: closeButtonBg
|
||||
anchors.margins: -1
|
||||
opacity: closeButtonBg.opacity
|
||||
border.width: 1
|
||||
radius: closeButtonBg.radius + 1
|
||||
border.color: Looks.colors.bg2Border
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
id: closeButtonBg
|
||||
z: 1
|
||||
opacity: reusableCloseButton.hovered ? 1 : 0
|
||||
color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: FluentIcon {
|
||||
z: 2
|
||||
anchors.centerIn: parent
|
||||
icon: "dismiss"
|
||||
implicitSize: 10
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Kirigami.Icon {
|
||||
id: root
|
||||
required property string icon
|
||||
property bool filled: false
|
||||
property alias monochrome: root.isMask
|
||||
// Should be 16, but it appears the icons have some padding,
|
||||
// Unlike the Windows-only Segoe UI icons, the open source FluentUI ones are hella small
|
||||
property int implicitSize: 20
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
source: icon === "" ? "" : `${Looks.iconsPath}/${root.icon}${filled ? "-filled" : ""}.svg`
|
||||
fallback: root.icon
|
||||
roundToIconSize: false
|
||||
color: Looks.colors.fg
|
||||
isMask: true
|
||||
animated: true
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: "transparent"
|
||||
|
||||
implicitWidth: 358
|
||||
implicitHeight: 47
|
||||
}
|
||||
266
surfaces/quickshell/ii-base/modules/waffle/looks/Looks.qml
Normal file
266
surfaces/quickshell/ii-base/modules/waffle/looks/Looks.qml
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
pragma Singleton
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
property QtObject darkColors
|
||||
property QtObject lightColors
|
||||
property QtObject colors
|
||||
property QtObject radius
|
||||
property QtObject font
|
||||
property QtObject transition
|
||||
property string iconsPath: `${Directories.assetsPath}/icons/fluent`
|
||||
property bool dark: Appearance.m3colors.darkmode
|
||||
|
||||
readonly property bool transparencyEnabled: Config.options.appearance.transparency.enable
|
||||
property real backgroundTransparency: transparencyEnabled ? 0.16 : 0
|
||||
property real panelBackgroundTransparency: transparencyEnabled ? 0.14 : 0
|
||||
property real panelLayerTransparency: root.dark ? 0.9 : 0.7
|
||||
property real contentTransparency: root.dark ? 0.87 : 0.5
|
||||
function applyBackgroundTransparency(col) {
|
||||
return ColorUtils.applyAlpha(col, 1 - root.backgroundTransparency)
|
||||
}
|
||||
function applyContentTransparency(col) {
|
||||
return ColorUtils.applyAlpha(col, 1 - root.contentTransparency)
|
||||
}
|
||||
lightColors: QtObject {
|
||||
id: lightColors
|
||||
property color bgPanelBody: "#F2F2F2"
|
||||
property color bgPanelSeparator: "#E0E0E0"
|
||||
property color bg0: "#EEEEEE"
|
||||
property color bg0Border: '#BEBEBE'
|
||||
property color bg1Base: "#F7F7F7"
|
||||
property color bg1: "#F7F7F7"
|
||||
property color bg1Hover: "#F7F7F7"
|
||||
property color bg1Active: '#EFEFEF'
|
||||
property color bg1Border: '#E9E9E9'
|
||||
property color bg2: "#FBFBFB"
|
||||
property color bg2Base: "#FBFBFB"
|
||||
property color bg2Hover: '#ffffff'
|
||||
property color bg2Active: '#eeeeee'
|
||||
property color bg2Border: '#E0E0E0'
|
||||
property color subfg: "#5C5C5C"
|
||||
property color fg: "#000000"
|
||||
property color fg1: "#626262"
|
||||
property color inactiveIcon: "#C4C4C4"
|
||||
property color controlBgInactive: '#555458'
|
||||
property color controlBg: '#807F85'
|
||||
property color controlBgHover: '#57575B'
|
||||
property color controlFg: "#FFFFFF"
|
||||
property color accentUnfocused: "#848484"
|
||||
property color link: "#235CCF"
|
||||
property color inputBg: ColorUtils.transparentize(bg0, 0.4)
|
||||
}
|
||||
darkColors: QtObject {
|
||||
id: darkColors
|
||||
property color bgPanelBody: '#242424'
|
||||
property color bgPanelSeparator: "#191919"
|
||||
property color bg0: "#1C1C1C"
|
||||
property color bg0Border: "#404040"
|
||||
property color bg1Base: '#2C2C2C'
|
||||
property color bg1: '#2C2C2C'
|
||||
property color bg1Hover: "#292929"
|
||||
property color bg1Active: '#252525'
|
||||
property color bg1Border: '#bebebe'
|
||||
property color bg2Base: "#313131"
|
||||
property color bg2: '#313131'
|
||||
property color bg2Hover: '#363636'
|
||||
property color bg2Active: '#2B2B2B'
|
||||
property color bg2Border: '#404040'
|
||||
property color subfg: "#CED1D7"
|
||||
property color fg: "#FFFFFF"
|
||||
property color fg1: "#D1D1D1"
|
||||
property color inactiveIcon: "#494949"
|
||||
property color controlBgInactive: "#CDCECF"
|
||||
property color controlBg: "#9B9B9B"
|
||||
property color controlBgHover: "#CFCED1"
|
||||
property color controlFg: "#454545"
|
||||
property color accentUnfocused: "#989898"
|
||||
property color link: "#A7C9FC"
|
||||
property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5)
|
||||
}
|
||||
colors: QtObject {
|
||||
id: colors
|
||||
// Special
|
||||
property color shadow: ColorUtils.transparentize('#161616', 0.62)
|
||||
property color ambientShadow: ColorUtils.transparentize("#000000", 0.75)
|
||||
property color bgPanelFooterBase: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bgPanelFooterBackground: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency)
|
||||
property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBackground, root.panelLayerTransparency)
|
||||
property color bgPanelBodyBase: root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody
|
||||
property color bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBackground,bgPanelBodyBase, 1 - root.panelLayerTransparency)
|
||||
property color bgPanelSeparator: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, 1 - root.panelBackgroundTransparency)
|
||||
// Layer 0
|
||||
property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
|
||||
property color bg0: ColorUtils.transparentize(bg0Base, root.backgroundTransparency)
|
||||
property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency)
|
||||
// Layer 1
|
||||
property color bg1Base: root.dark ? root.darkColors.bg1 : root.lightColors.bg1
|
||||
property color bg1: ColorUtils.solveOverlayColor(bg0Base, bg1Base, 1 - root.contentTransparency)
|
||||
property color bg1Hover: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, 1 - root.contentTransparency)
|
||||
property color bg1Active: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, 1 - root.contentTransparency)
|
||||
property color bg1Border: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, 1 - root.contentTransparency)
|
||||
// Layer 2
|
||||
property color bg2Base: root.dark ? root.darkColors.bg2 : root.lightColors.bg2
|
||||
property color bg2: ColorUtils.solveOverlayColor(bgPanelBodyBase, bg2Base, 1 - root.contentTransparency)
|
||||
property color bg2Hover: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, 1 - root.contentTransparency)
|
||||
property color bg2Active: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, 1 - root.contentTransparency)
|
||||
property color bg2Border: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, 1 - root.contentTransparency)
|
||||
// Foreground / Text
|
||||
property color subfg: root.dark ? root.darkColors.subfg : root.lightColors.subfg
|
||||
property color fg: root.dark ? root.darkColors.fg : root.lightColors.fg
|
||||
property color fg1: root.dark ? root.darkColors.fg1 : root.lightColors.fg1
|
||||
property color inactiveIcon: root.dark ? root.darkColors.inactiveIcon : root.lightColors.inactiveIcon
|
||||
property color link: root.dark ? root.darkColors.link : root.lightColors.link
|
||||
// Controls
|
||||
property color controlBgInactive: root.dark ? root.darkColors.controlBgInactive : root.lightColors.controlBgInactive
|
||||
property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg
|
||||
property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover
|
||||
property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg
|
||||
property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg
|
||||
property color danger: "#C42B1C"
|
||||
property color dangerActive: "#B62D1F"
|
||||
property color warning: "#FF9900"
|
||||
// Accent
|
||||
property color accent: Appearance.colors.colPrimary
|
||||
property color accentHover: Appearance.colors.colPrimaryHover
|
||||
property color accentActive: Appearance.colors.colPrimaryActive
|
||||
property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused
|
||||
property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000"
|
||||
property color selection: Appearance.colors.colPrimaryContainer
|
||||
property color selectionFg: Appearance.colors.colOnPrimaryContainer
|
||||
}
|
||||
|
||||
radius: QtObject {
|
||||
id: radius
|
||||
property int none: 0
|
||||
property int small: 2
|
||||
property int medium: 4
|
||||
property int large: 8
|
||||
property int xLarge: 12
|
||||
}
|
||||
|
||||
font: QtObject {
|
||||
id: font
|
||||
property QtObject family: QtObject {
|
||||
property string ui: "Noto Sans"
|
||||
}
|
||||
property QtObject weight: QtObject { // Noto is not Segoe, so we might use slightly different weights
|
||||
property int thin: Font.Normal
|
||||
property int regular: Font.Medium
|
||||
property int strong: Font.DemiBold
|
||||
property int stronger: (Font.DemiBold + 2*Font.Bold) / 3
|
||||
property int strongest: Font.Bold
|
||||
}
|
||||
property QtObject pixelSize: QtObject {
|
||||
property real normal: 11
|
||||
property real large: 13
|
||||
property real larger: 15
|
||||
property real xlarger: 17
|
||||
}
|
||||
property QtObject variableAxes: QtObject {
|
||||
property var ui: ({
|
||||
"wdth": 25
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
transition: QtObject {
|
||||
id: transition
|
||||
|
||||
property int velocity: 850
|
||||
|
||||
property QtObject easing: QtObject {
|
||||
property QtObject bezierCurve: QtObject {
|
||||
readonly property list<real> easeInOut: [0.42,0.00,0.58,1.00,1,1]
|
||||
readonly property list<real> easeIn: [0,1,1,1,1,1]
|
||||
readonly property list<real> easeOut: [1,0,1,1,1,1]
|
||||
}
|
||||
}
|
||||
|
||||
property Component color: Component {
|
||||
ColorAnimation {
|
||||
duration: 80
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component opacity: Component {
|
||||
NumberAnimation {
|
||||
duration: 120
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component resize: Component { // TODO: better curve needed
|
||||
NumberAnimation {
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component enter: Component {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component exit: Component {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
}
|
||||
|
||||
property Component move: Component {
|
||||
NumberAnimation {
|
||||
duration: 170
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeInOut
|
||||
}
|
||||
}
|
||||
|
||||
property Component rotate: Component {
|
||||
NumberAnimation {
|
||||
duration: 170
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeInOut
|
||||
}
|
||||
}
|
||||
|
||||
property Component anchor: Component {
|
||||
AnchorAnimation {
|
||||
duration: 160
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component longMovement: Component {
|
||||
NumberAnimation {
|
||||
duration: 1000
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
|
||||
property Component scroll: Component {
|
||||
NumberAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: [0.0, 0.0, 0.25, 1.0, 1, 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import Qt.labs.synchronizer
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property bool showArrows: true
|
||||
property int currentIndex: 0
|
||||
property int count: 1
|
||||
signal clicked(int index)
|
||||
signal increasePage()
|
||||
signal decreasePage()
|
||||
|
||||
visible: count > 1
|
||||
spacing: 6
|
||||
|
||||
NavigationArrow {
|
||||
visible: root.showArrows
|
||||
down: false
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: root.count
|
||||
delegate: MouseArea {
|
||||
id: pageIndicator
|
||||
required property int index
|
||||
hoverEnabled: true
|
||||
onClicked: root.clicked(index);
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitWidth: 6
|
||||
implicitHeight: 6
|
||||
|
||||
Circle {
|
||||
anchors.centerIn: parent
|
||||
diameter: (index === root.currentIndex || pageIndicator.containsMouse) && !pageIndicator.pressed ? 6 : 4
|
||||
color: pageIndicator.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NavigationArrow {
|
||||
visible: root.showArrows
|
||||
down: true
|
||||
}
|
||||
|
||||
component NavigationArrow: FluentIcon {
|
||||
id: navArrow
|
||||
required property bool down
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
implicitHeight: 12
|
||||
implicitWidth: 12 - (2 * upArea.containsPress)
|
||||
icon: down ? "caret-down" : "caret-up"
|
||||
color: upArea.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg
|
||||
filled: true
|
||||
opacity: ((down && root.currentIndex < root.count - 1) || (!down && root.currentIndex > 0)) ? 1 : 0
|
||||
MouseArea {
|
||||
id: upArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: navArrow.down ? root.increasePage() : root.decreasePage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
required property var target
|
||||
z: 0
|
||||
|
||||
anchors {
|
||||
fill: target
|
||||
margins: -border.width
|
||||
}
|
||||
|
||||
border.color: Looks.colors.ambientShadow
|
||||
border.width: 1
|
||||
color: "transparent"
|
||||
radius: target.radius + border.width
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
|
||||
Kirigami.Icon {
|
||||
id: root
|
||||
required property string iconName
|
||||
property bool separateLightDark: false
|
||||
property bool tryCustomIcon: true
|
||||
|
||||
property real implicitSize: 26
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
|
||||
animated: true
|
||||
roundToIconSize: false
|
||||
fallback: root.iconName
|
||||
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
|
||||
|
||||
color: Looks.colors.fg
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
signal closed
|
||||
|
||||
required property Item contentItem
|
||||
property real visualMargin: 12
|
||||
property int closeAnimDuration: 150
|
||||
property bool revealFromSides: false
|
||||
property bool revealFromLeft: true
|
||||
|
||||
function close() {
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
|
||||
implicitHeight: contentItem.implicitHeight + visualMargin * 2
|
||||
implicitWidth: contentItem.implicitWidth + visualMargin * 2
|
||||
|
||||
focus: true
|
||||
Keys.onPressed: event => { // Esc to close
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
content.close();
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: panelContent
|
||||
anchors {
|
||||
left: (root.revealFromSides && !root.revealFromLeft) ? undefined : parent.left
|
||||
right: (root.revealFromSides && root.revealFromLeft) ? undefined : parent.right
|
||||
top: (!root.revealFromSides && root.barAtBottom) ? undefined : parent.top
|
||||
bottom: (!root.revealFromSides && !root.barAtBottom) ? undefined : parent.bottom
|
||||
// Opening anim
|
||||
bottomMargin: (!root.revealFromSides && root.barAtBottom) ? sourceEdgeMargin : root.visualMargin
|
||||
topMargin: (!root.revealFromSides && !root.barAtBottom) ? sourceEdgeMargin : root.visualMargin
|
||||
leftMargin: (root.revealFromSides && root.revealFromLeft) ? sideEdgeMargin : root.visualMargin
|
||||
rightMargin: (root.revealFromSides && !root.revealFromLeft) ? sideEdgeMargin : root.visualMargin
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
openAnim.start();
|
||||
}
|
||||
|
||||
property real sourceEdgeMargin: -(implicitHeight + root.visualMargin)
|
||||
property real sideEdgeMargin: -(implicitWidth + root.visualMargin)
|
||||
OpenAnim {
|
||||
id: openAnim
|
||||
properties: "sourceEdgeMargin, sideEdgeMargin"
|
||||
}
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
ParallelAnimation {
|
||||
CloseAnim {
|
||||
property: "sourceEdgeMargin"
|
||||
to: -(implicitHeight + root.visualMargin)
|
||||
}
|
||||
CloseAnim {
|
||||
property: "sideEdgeMargin"
|
||||
to: -(implicitWidth + root.visualMargin)
|
||||
}
|
||||
}
|
||||
ScriptAction {
|
||||
script: {
|
||||
root.closed();
|
||||
}
|
||||
}
|
||||
}
|
||||
implicitWidth: root.contentItem.implicitWidth
|
||||
implicitHeight: root.contentItem.implicitHeight
|
||||
children: [root.contentItem]
|
||||
}
|
||||
|
||||
component OpenAnim: PropertyAnimation {
|
||||
target: panelContent
|
||||
to: root.visualMargin
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
component CloseAnim: PropertyAnimation {
|
||||
target: panelContent
|
||||
duration: root.closeAnimDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
colBackground: Looks.colors.bg2
|
||||
colBackgroundHover: Looks.colors.bg2Hover
|
||||
colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBorder: Looks.colors.bg2Border
|
||||
property color colBorderToggled: Looks.colors.accent
|
||||
border.color: checked ? colBorderToggled : colBorder
|
||||
border.width: root.pressed ? 2 : 1
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
implicitHeight: 36
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg1Hover
|
||||
property color colBackgroundActive: Looks.colors.bg1Active
|
||||
property color color
|
||||
property color colForeground: Looks.colors.fg
|
||||
color: {
|
||||
if (!root.enabled) return colBackground;
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive
|
||||
} else if ((root.hovered && !root.down) || root.checked) {
|
||||
return root.colBackgroundHover
|
||||
} else {
|
||||
return root.colBackground
|
||||
}
|
||||
}
|
||||
property alias radius: background.radius
|
||||
|
||||
background: Rectangle {
|
||||
id: background
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
}
|
||||
}
|
||||
158
surfaces/quickshell/ii-base/modules/waffle/looks/WButton.qml
Normal file
158
surfaces/quickshell/ii-base/modules/waffle/looks/WButton.qml
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
// Generic button with background
|
||||
Button {
|
||||
id: root
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBackgroundToggled: Looks.colors.accent
|
||||
property color colBackgroundToggledHover: Looks.colors.accentHover
|
||||
property color colBackgroundToggledActive: Looks.colors.accentActive
|
||||
property color colForeground: Looks.colors.fg
|
||||
property color colForegroundToggled: Looks.colors.accentFg
|
||||
property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4)
|
||||
property alias backgroundOpacity: backgroundRect.opacity
|
||||
property color color: {
|
||||
if (!root.enabled) return colBackground;
|
||||
if (root.checked) {
|
||||
if (root.down) {
|
||||
return root.colBackgroundToggledActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundToggledHover;
|
||||
} else {
|
||||
return root.colBackgroundToggled;
|
||||
}
|
||||
}
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
property color fgColor: {
|
||||
if (!root.enabled) return root.colForegroundDisabled
|
||||
if (root.checked) return root.colForegroundToggled
|
||||
if (root.enabled) return root.colForeground
|
||||
return root.colForeground
|
||||
}
|
||||
property alias horizontalAlignment: buttonText.horizontalAlignment
|
||||
font {
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
// Hover stuff
|
||||
signal hoverTimedOut
|
||||
property bool shouldShowTooltip: false
|
||||
ToolTip.delay: 400
|
||||
property Timer hoverTimer: Timer {
|
||||
id: hoverTimer
|
||||
running: root.hovered
|
||||
interval: root.ToolTip.delay
|
||||
onTriggered: {
|
||||
root.hoverTimedOut();
|
||||
}
|
||||
}
|
||||
onHoverTimedOut: {
|
||||
root.shouldShowTooltip = true;
|
||||
}
|
||||
onHoveredChanged: {
|
||||
if (!root.hovered) {
|
||||
root.shouldShowTooltip = false;
|
||||
root.hoverTimer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
property alias monochromeIcon: buttonIcon.monochrome
|
||||
property alias buttonSpacing: contentLayout.spacing
|
||||
property bool forceShowIcon: false
|
||||
|
||||
property var altAction: () => {}
|
||||
property var middleClickAction: () => {}
|
||||
|
||||
property real inset: 0
|
||||
topInset: inset
|
||||
bottomInset: inset
|
||||
leftInset: inset
|
||||
rightInset: inset
|
||||
property alias radius: backgroundRect.radius
|
||||
property alias topLeftRadius: backgroundRect.topLeftRadius
|
||||
property alias topRightRadius: backgroundRect.topRightRadius
|
||||
property alias bottomLeftRadius: backgroundRect.bottomLeftRadius
|
||||
property alias bottomRightRadius: backgroundRect.bottomRightRadius
|
||||
property alias border: backgroundRect.border
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 6
|
||||
implicitHeight: contentItem.implicitHeight + verticalPadding * 2 + topInset + bottomInset
|
||||
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + leftInset + rightInset
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.RightButton | Qt.MiddleButton
|
||||
onClicked: event => {
|
||||
if (event.button === Qt.LeftButton)
|
||||
root.clicked();
|
||||
if (event.button === Qt.RightButton)
|
||||
root.altAction();
|
||||
if (event.button === Qt.MiddleButton)
|
||||
root.middleClickAction();
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: root.inset
|
||||
}
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: root.horizontalPadding
|
||||
rightMargin: root.horizontalPadding
|
||||
}
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 18
|
||||
Layout.leftMargin: root.iconLeftMargin
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
icon: root.icon.name
|
||||
color: root.fgColor
|
||||
visible: root.icon.name !== ""
|
||||
}
|
||||
WText {
|
||||
id: buttonText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font: root.font
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
property bool animateChoiceHighlight: true
|
||||
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
horizontalPadding: 10
|
||||
verticalPadding: 11
|
||||
buttonSpacing: 8
|
||||
|
||||
color: {
|
||||
if (root.checked) {
|
||||
if (root.down) {
|
||||
return root.colBackgroundHover;
|
||||
} else if (root.hovered && !root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else {
|
||||
return root.colBackgroundHover;
|
||||
}
|
||||
}
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered && !root.down) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
fgColor: colForeground
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
Behavior on color {
|
||||
enabled: root.animateChoiceHighlight
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
|
||||
WFadeLoader {
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
shown: root.checked
|
||||
sourceComponent: Rectangle {
|
||||
implicitWidth: 3
|
||||
implicitHeight: 3
|
||||
radius: width / 2
|
||||
color: Looks.colors.accent
|
||||
property bool forceZeroHeight: true
|
||||
height: forceZeroHeight ? 0 : Math.max(16, root.background.height - 18 * 2)
|
||||
Component.onCompleted: {
|
||||
forceZeroHeight = false;
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
enabled: root.animateChoiceHighlight
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
import qs.modules.common
|
||||
|
||||
// Yes, this is (mostly) a copy of FadeLoader.
|
||||
// The animation of a Behavior cannot be changed... I'd love to be proven wrong.
|
||||
Loader {
|
||||
id: root
|
||||
property bool shown: true
|
||||
property alias fade: opacityBehavior.enabled
|
||||
property alias animation: opacityBehavior.animation
|
||||
opacity: shown ? 1 : 0
|
||||
visible: opacity > 0
|
||||
active: opacity > 0
|
||||
|
||||
Behavior on opacity {
|
||||
id: opacityBehavior
|
||||
animation: Looks.transition.opacity.createObject(null)
|
||||
}
|
||||
}
|
||||
187
surfaces/quickshell/ii-base/modules/waffle/looks/WIcons.qml
Normal file
187
surfaces/quickshell/ii-base/modules/waffle/looks/WIcons.qml
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
pragma Singleton
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Services.UPower
|
||||
import qs.services
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
function pathForName(iconName) {
|
||||
return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`);
|
||||
}
|
||||
|
||||
function wifiIconForStrength(strength) {
|
||||
if (strength > 75)
|
||||
return "wifi-1";
|
||||
if (strength > 50)
|
||||
return "wifi-2";
|
||||
if (strength > 25)
|
||||
return "wifi-3";
|
||||
return "wifi-4";
|
||||
}
|
||||
|
||||
property string internetIcon: {
|
||||
if (Network.ethernet)
|
||||
return "ethernet";
|
||||
if (Network.wifiEnabled) {
|
||||
const strength = Network.networkStrength;
|
||||
return wifiIconForStrength(strength);
|
||||
}
|
||||
if (Network.wifiStatus === "connecting")
|
||||
return "wifi-4";
|
||||
if (Network.wifiStatus === "disconnected")
|
||||
return "wifi-off";
|
||||
if (Network.wifiStatus === "disabled")
|
||||
return "wifi-off";
|
||||
return "wifi-warning";
|
||||
}
|
||||
|
||||
property string batteryIcon: {
|
||||
if (Battery.isCharging)
|
||||
return "battery-charge";
|
||||
if (Battery.isCriticalAndNotCharging)
|
||||
return "battery-warning";
|
||||
if (Battery.percentage >= 0.9)
|
||||
return "battery-full";
|
||||
return `battery-0`;
|
||||
}
|
||||
|
||||
property string batteryLevelIcon: {
|
||||
const discreteLevel = Math.ceil(Battery.percentage * 10);
|
||||
return `battery-${discreteLevel > 9 ? "full" : discreteLevel}`;
|
||||
}
|
||||
|
||||
property string volumeIcon: {
|
||||
const muted = Audio.sink?.audio.muted ?? false;
|
||||
const volume = Audio.sink?.audio.volume ?? 0;
|
||||
if (muted)
|
||||
return "speaker-mute";
|
||||
if (volume == 0)
|
||||
return "speaker-none";
|
||||
if (volume < 0.5)
|
||||
return "speaker-1";
|
||||
return "speaker";
|
||||
}
|
||||
|
||||
property string micIcon: {
|
||||
const muted = Audio.source?.audio.muted ?? false;
|
||||
return muted ? "mic-off" : "mic";
|
||||
}
|
||||
|
||||
property string bluetoothIcon: BluetoothStatus.connected ? "bluetooth-connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth-disabled"
|
||||
|
||||
property string nightLightIcon: Hyprsunset.temperatureActive ? "weather-moon" : "weather-moon-off"
|
||||
|
||||
property string notificationsIcon: Notifications.silent ? "alert-snooze" : "alert"
|
||||
|
||||
property string powerProfileIcon: {
|
||||
switch (PowerProfiles.profile) {
|
||||
case PowerProfile.PowerSaver:
|
||||
return "leaf-two";
|
||||
case PowerProfile.Balanced:
|
||||
return "flash-on";
|
||||
case PowerProfile.Performance:
|
||||
return "fire";
|
||||
}
|
||||
}
|
||||
|
||||
function audioDeviceIcon(node) {
|
||||
if (!node.isSink)
|
||||
return "mic-on";
|
||||
const monitor = /monitor|hdmi/i;
|
||||
const headphones = /headset|headphone|bluez|wireless/i;
|
||||
const speakers = /speaker|output/i;
|
||||
if (monitor.test(node.nickname) || monitor.test(node.description) || monitor.test(node.name)) {
|
||||
return "desktop-speaker";
|
||||
}
|
||||
if (headphones.test(node.nickname) || headphones.test(node.description) || headphones.test(node.name)) {
|
||||
return "headphones";
|
||||
}
|
||||
if (speakers.test(node.nickname) || speakers.test(node.description) || speakers.test(node.name)) {
|
||||
return "speaker";
|
||||
}
|
||||
return "speaker";
|
||||
}
|
||||
|
||||
function audioAppIcon(node) {
|
||||
let icon;
|
||||
icon = AppSearch.guessIcon(node?.properties["application.icon-name"] ?? "");
|
||||
if (AppSearch.iconExists(icon))
|
||||
return icon;
|
||||
icon = AppSearch.guessIcon(node?.properties["node.name"] ?? "");
|
||||
return icon;
|
||||
}
|
||||
|
||||
function bluetoothDeviceIcon(device) {
|
||||
const systemIconName = device?.icon || "";
|
||||
if (systemIconName.includes("headset") || systemIconName.includes("headphones"))
|
||||
return "headphones";
|
||||
if (systemIconName.includes("audio"))
|
||||
return "speaker";
|
||||
if (systemIconName.includes("phone"))
|
||||
return "phone";
|
||||
if (systemIconName.includes("mouse"))
|
||||
return "bluetooth";
|
||||
if (systemIconName.includes("keyboard"))
|
||||
return "keyboard";
|
||||
return "bluetooth";
|
||||
}
|
||||
|
||||
function fluentFromMaterial(icon) {
|
||||
switch (icon) {
|
||||
case "calculate":
|
||||
return "calculator";
|
||||
case "keyboard_return":
|
||||
return "arrow-enter-left";
|
||||
case "open_in_new":
|
||||
return "open";
|
||||
case "settings_suggest":
|
||||
return "wand";
|
||||
case "terminal":
|
||||
return "app-generic";
|
||||
case "travel_explore":
|
||||
return "globe-search";
|
||||
case "keep":
|
||||
return "pin";
|
||||
case "keep_off":
|
||||
return "pin-off";
|
||||
default:
|
||||
return "apps";
|
||||
}
|
||||
}
|
||||
|
||||
function guessIconForName(name) {
|
||||
const lowerName = name.toLowerCase();
|
||||
if (lowerName.includes("app") || lowerName.includes("desktop"))
|
||||
return "apps";
|
||||
if (lowerName.includes("news"))
|
||||
return "news";
|
||||
if (lowerName.includes("new") || lowerName.includes("create") || lowerName.includes("add"))
|
||||
return "add";
|
||||
if (lowerName.includes("open"))
|
||||
return "open";
|
||||
if (lowerName.includes("friends") || lowerName.includes("contact") || lowerName.includes("family"))
|
||||
return "people";
|
||||
if (lowerName.includes("community"))
|
||||
return "people-team";
|
||||
if (lowerName.includes("library"))
|
||||
return "library";
|
||||
if (lowerName.includes("setting"))
|
||||
return "settings";
|
||||
if (lowerName.includes("gallery"))
|
||||
return "image-copy";
|
||||
if (lowerName.includes("server"))
|
||||
return "server";
|
||||
if (lowerName.includes("picture") || lowerName.includes("photo") || lowerName.includes("image"))
|
||||
return "image";
|
||||
if (lowerName.includes("store") || lowerName.includes("shop"))
|
||||
return "store-microsoft";
|
||||
if (lowerName.includes("record") || lowerName.includes("capture"))
|
||||
return "record";
|
||||
if (lowerName.includes("screen") || lowerName.includes("display") || lowerName.includes("monitor") || lowerName.includes("desktop"))
|
||||
return "desktop";
|
||||
|
||||
return "apps";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import QtQuick
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.services.network
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StyledIndeterminateProgressBar {
|
||||
id: progressBar
|
||||
implicitHeight: 3
|
||||
background: null
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: progressBar.width
|
||||
height: progressBar.height
|
||||
radius: progressBar.height / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
ListView {
|
||||
id: root
|
||||
|
||||
boundsBehavior: Flickable.DragOverBounds
|
||||
|
||||
ScrollBar.vertical: WScrollBar {}
|
||||
|
||||
displaced: Transition {
|
||||
animations: [Looks.transition.enter.createObject(this, {
|
||||
property: "y"
|
||||
})]
|
||||
}
|
||||
|
||||
}
|
||||
104
surfaces/quickshell/ii-base/modules/waffle/looks/WMenu.qml
Normal file
104
surfaces/quickshell/ii-base/modules/waffle/looks/WMenu.qml
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Menu {
|
||||
id: root
|
||||
|
||||
property bool downDirection: false
|
||||
property bool hasIcons: false // TODO: implement
|
||||
|
||||
property color color: Looks.colors.bg1Base
|
||||
property alias backgroundPane: bgPane
|
||||
|
||||
implicitWidth: background.implicitWidth + margins * 2
|
||||
implicitHeight: background.implicitHeight + margins * 2
|
||||
margins: 10
|
||||
padding: 3
|
||||
property real sourceEdgeMargin: -implicitHeight
|
||||
clip: true
|
||||
|
||||
enter: Transition {
|
||||
NumberAnimation {
|
||||
property: "sourceEdgeMargin"
|
||||
from: -root.implicitHeight
|
||||
to: root.margins
|
||||
duration: 200
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
}
|
||||
}
|
||||
exit: Transition {
|
||||
NumberAnimation {
|
||||
property: "sourceEdgeMargin"
|
||||
from: root.margins
|
||||
to: -root.implicitHeight
|
||||
duration: 150
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
|
||||
}
|
||||
}
|
||||
|
||||
background: Item {
|
||||
id: bgItem
|
||||
implicitWidth: bgPane.implicitWidth
|
||||
implicitHeight: bgPane.implicitHeight
|
||||
WPane {
|
||||
id: bgPane
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.downDirection ? parent.top : undefined
|
||||
bottom: root.downDirection ? undefined : parent.bottom
|
||||
margins: root.margins
|
||||
topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins
|
||||
bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin
|
||||
}
|
||||
contentItem: Rectangle {
|
||||
color: root.color
|
||||
implicitWidth: menuListView.implicitWidth + root.padding * 2
|
||||
implicitHeight: root.contentItem.implicitHeight + root.padding * 2
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
menuListView.itemAtIndex(0)?.forceActiveFocus();
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: menuListView.implicitWidth
|
||||
implicitHeight: menuListView.implicitHeight
|
||||
WListView {
|
||||
id: menuListView
|
||||
interactive: contentHeight > height
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: root.downDirection ? parent.top : undefined
|
||||
bottom: root.downDirection ? undefined : parent.bottom
|
||||
margins: root.margins // ????
|
||||
topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins
|
||||
bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin
|
||||
}
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
implicitWidth: Array.from({
|
||||
length: count
|
||||
}, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b)
|
||||
|
||||
model: root.contentModel
|
||||
}
|
||||
}
|
||||
|
||||
delegate: WMenuItem {
|
||||
id: menuItemDelegate
|
||||
}
|
||||
}
|
||||
127
surfaces/quickshell/ii-base/modules/waffle/looks/WMenuItem.qml
Normal file
127
surfaces/quickshell/ii-base/modules/waffle/looks/WMenuItem.qml
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
MenuItem {
|
||||
id: root
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBackgroundToggled: Looks.colors.bg2Hover
|
||||
property color colBackgroundToggledHover: Looks.colors.bg2Active
|
||||
property color colBackgroundToggledActive: Looks.colors.bg2Hover
|
||||
property color colForeground: Looks.colors.fg
|
||||
property color colForegroundToggled: Looks.colors.fg
|
||||
property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4)
|
||||
property color color: {
|
||||
if (!root.enabled)
|
||||
return colBackground;
|
||||
if (root.checked) {
|
||||
if (root.down) {
|
||||
return root.colBackgroundToggledActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundToggledHover;
|
||||
} else {
|
||||
return root.colBackgroundToggled;
|
||||
}
|
||||
}
|
||||
if (root.down) {
|
||||
return root.colBackgroundActive;
|
||||
} else if (root.hovered) {
|
||||
return root.colBackgroundHover;
|
||||
} else {
|
||||
return root.colBackground;
|
||||
}
|
||||
}
|
||||
property color fgColor: {
|
||||
if (root.checked)
|
||||
return root.colForegroundToggled;
|
||||
if (root.enabled)
|
||||
return root.colForeground;
|
||||
return root.colForegroundDisabled;
|
||||
}
|
||||
|
||||
property real inset: 2
|
||||
topInset: inset
|
||||
bottomInset: inset
|
||||
leftInset: inset
|
||||
rightInset: inset
|
||||
horizontalPadding: 11
|
||||
|
||||
width: ListView.view?.width
|
||||
height: visible ? implicitHeight : 0
|
||||
|
||||
background: Rectangle {
|
||||
id: backgroundRect
|
||||
radius: Looks.radius.medium
|
||||
color: root.color
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset
|
||||
implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding
|
||||
|
||||
contentItem: Item {
|
||||
implicitWidth: contentLayout.implicitWidth
|
||||
implicitHeight: contentLayout.implicitHeight
|
||||
|
||||
RowLayout {
|
||||
id: contentLayout
|
||||
anchors.fill: parent
|
||||
spacing: 12
|
||||
FluentIcon {
|
||||
id: buttonIcon
|
||||
monochrome: true
|
||||
implicitSize: 20
|
||||
Layout.fillWidth: false
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
color: root.fgColor
|
||||
visible: root.icon.name !== ""
|
||||
icon: root.icon.name
|
||||
}
|
||||
WText {
|
||||
id: buttonText
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
|
||||
text: root.text
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
|
||||
WFadeLoader {
|
||||
anchors {
|
||||
verticalCenter: parent.verticalCenter
|
||||
left: parent.left
|
||||
leftMargin: -root.leftPadding + width
|
||||
}
|
||||
shown: root.checked
|
||||
sourceComponent: Rectangle {
|
||||
implicitWidth: 3
|
||||
implicitHeight: 3
|
||||
radius: width / 2
|
||||
color: Looks.colors.accent
|
||||
property bool forceZeroHeight: true
|
||||
height: forceZeroHeight ? 0 : Math.max(root.down ? 10 : 16, root.background.height - 18 * 2)
|
||||
Component.onCompleted: {
|
||||
forceZeroHeight = false;
|
||||
}
|
||||
|
||||
Behavior on height {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import QtQuick
|
||||
import qs
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
property real radius: Looks.radius.medium
|
||||
hoverEnabled: true
|
||||
|
||||
property color colBackground: ColorUtils.transparentize(Looks.colors.bg2)
|
||||
property color colBackgroundHover: Looks.colors.bg2Hover
|
||||
property color colBackgroundActive: Looks.colors.bg2Active
|
||||
property color colBorder: ColorUtils.transparentize(Looks.colors.bg2Border)
|
||||
property color colBorderHover: Looks.colors.bg2Border
|
||||
|
||||
property color color: {
|
||||
if (containsMouse) {
|
||||
return pressed ? colBackgroundActive : colBackgroundHover;
|
||||
} else {
|
||||
return colBackground;
|
||||
}
|
||||
}
|
||||
|
||||
property color borderColor: {
|
||||
if (containsMouse) {
|
||||
return colBorderHover;
|
||||
} else {
|
||||
return colBorder;
|
||||
}
|
||||
}
|
||||
|
||||
property Item background: Rectangle {
|
||||
id: bgRect
|
||||
parent: root
|
||||
anchors.fill: parent
|
||||
color: root.color
|
||||
radius: root.radius
|
||||
|
||||
border.color: root.borderColor
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
60
surfaces/quickshell/ii-base/modules/waffle/looks/WPane.qml
Normal file
60
surfaces/quickshell/ii-base/modules/waffle/looks/WPane.qml
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property Item contentItem
|
||||
property real radius: Looks.radius.large
|
||||
property alias color: contentRect.color
|
||||
property alias border: borderRect
|
||||
property alias borderColor: borderRect.border.color
|
||||
property alias borderWidth: borderRect.border.width
|
||||
|
||||
implicitWidth: borderRect.implicitWidth
|
||||
implicitHeight: borderRect.implicitHeight
|
||||
|
||||
WRectangularShadow {
|
||||
target: borderRect
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: borderRect
|
||||
z: 1
|
||||
|
||||
color: "transparent"
|
||||
radius: root.radius
|
||||
border.color: Looks.colors.bg2Border
|
||||
border.width: 1
|
||||
implicitWidth: contentItem.implicitWidth + border.width * 2
|
||||
implicitHeight: contentItem.implicitHeight + border.width * 2
|
||||
anchors.fill: contentRect
|
||||
anchors.margins: -border.width
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: contentRect
|
||||
anchors.centerIn: parent
|
||||
z: 0
|
||||
|
||||
color: Looks.colors.bgPanelFooterBackground
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
id: contentAreaMask
|
||||
width: contentRect.width
|
||||
height: contentRect.height
|
||||
radius: root.radius - borderRect.border.width
|
||||
}
|
||||
}
|
||||
children: [root.contentItem]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
import qs.modules.waffle.bar
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
|
||||
property alias iconName: iconContent.icon
|
||||
property alias iconSize: iconContent.implicitSize
|
||||
property alias monochrome: iconContent.monochrome
|
||||
implicitWidth: 40
|
||||
implicitHeight: 40
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
id: iconContent
|
||||
anchors.centerIn: parent
|
||||
implicitSize: 18
|
||||
icon: root.iconName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Rectangle {
|
||||
Layout.fillHeight: false
|
||||
Layout.fillWidth: true
|
||||
color: Looks.colors.bgPanelSeparator
|
||||
implicitHeight: 1
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
PopupToolTip {
|
||||
id: root
|
||||
|
||||
required property Item realContentItem
|
||||
realContentItem: WText {
|
||||
text: root.text
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
property real visualMargin: 11
|
||||
verticalPadding: 8
|
||||
horizontalPadding: 10
|
||||
verticalMargin: visualMargin
|
||||
horizontalMargin: visualMargin
|
||||
|
||||
contentItem: WToolTipContent {
|
||||
id: tooltipContent
|
||||
realContentItem: root.realContentItem
|
||||
horizontalPadding: root.horizontalPadding
|
||||
verticalPadding: root.verticalPadding
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Widgets
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
ProgressBar {
|
||||
id: root
|
||||
|
||||
Behavior on value {
|
||||
SmoothedAnimation {
|
||||
velocity: Looks.transition.velocity
|
||||
}
|
||||
}
|
||||
|
||||
implicitHeight: 4
|
||||
background: null
|
||||
|
||||
contentItem: Item {
|
||||
id: background
|
||||
|
||||
Rectangle {
|
||||
id: trackTrough
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
radius: root.implicitHeight / 2
|
||||
color: Looks.colors.controlBg
|
||||
implicitHeight: root.implicitHeight
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: trackHighlight
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
radius: root.implicitHeight / 2
|
||||
color: Looks.colors.accent
|
||||
implicitHeight: root.implicitHeight
|
||||
width: background.width * root.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
StyledRectangularShadow {
|
||||
blur: 10
|
||||
spread: 2
|
||||
offset: Qt.vector2d(0.0, 4)
|
||||
color: Looks.colors.shadow
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Item {
|
||||
default property Item contentItem
|
||||
property Item shadow: WRectangularShadow {
|
||||
target: contentItem
|
||||
}
|
||||
implicitWidth: contentItem.implicitWidth
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
|
||||
children: [shadow, contentItem]
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
|
||||
ScrollBar {
|
||||
id: root
|
||||
|
||||
policy: ScrollBar.AsNeeded
|
||||
active: hovered || pressed
|
||||
property color color: Looks.colors.controlBg
|
||||
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: root.active ? 4 : 2
|
||||
implicitHeight: root.visualSize
|
||||
radius: 9999
|
||||
color: root.color
|
||||
|
||||
opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0
|
||||
Behavior on opacity {
|
||||
animation: Looks.transition.opacity.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
105
surfaces/quickshell/ii-base/modules/waffle/looks/WSlider.qml
Normal file
105
surfaces/quickshell/ii-base/modules/waffle/looks/WSlider.qml
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell.Widgets
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Slider {
|
||||
id: root
|
||||
|
||||
property real trackWidth: 4
|
||||
property string tooltipContent: `${Math.round(value * 100)}`
|
||||
property bool scrollable: false
|
||||
stepSize: 0.02
|
||||
leftPadding: 0
|
||||
rightPadding: 0
|
||||
|
||||
implicitHeight: handle.implicitHeight
|
||||
|
||||
Behavior on value { // This makes the adjusted value (like volume) shift smoothly
|
||||
SmoothedAnimation {
|
||||
velocity: Looks.transition.velocity
|
||||
}
|
||||
}
|
||||
|
||||
background: MouseArea {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
|
||||
onWheel: (event) => {
|
||||
if (!root.scrollable) {
|
||||
event.accepted = false;
|
||||
return;
|
||||
}
|
||||
if (event.angleDelta.y > 0) {
|
||||
root.value = Math.min(root.value + root.stepSize, 1)
|
||||
root.moved()
|
||||
} else {
|
||||
root.value = Math.max(root.value - root.stepSize, 0)
|
||||
root.moved()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: trackHighlight
|
||||
anchors {
|
||||
left: parent.left
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
topLeftRadius: root.trackWidth / 2
|
||||
bottomLeftRadius: root.trackWidth / 2
|
||||
color: Looks.colors.accent
|
||||
implicitHeight: root.trackWidth
|
||||
width: background.width * root.visualPosition
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: trackTrough
|
||||
anchors {
|
||||
right: parent.right
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
topRightRadius: root.trackWidth / 2
|
||||
bottomRightRadius: root.trackWidth / 2
|
||||
color: Looks.colors.controlBg
|
||||
implicitHeight: root.trackWidth
|
||||
width: background.width * (1 - root.visualPosition)
|
||||
}
|
||||
}
|
||||
|
||||
handle: Circle {
|
||||
id: handle
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
x: (diameter / 2) + root.visualPosition * (root.width - diameter) - (diameter / 2)
|
||||
diameter: 20
|
||||
color: Looks.colors.controlFg
|
||||
|
||||
MouseArea {
|
||||
id: handleMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: Qt.NoButton
|
||||
}
|
||||
|
||||
Circle {
|
||||
anchors.centerIn: parent
|
||||
diameter: root.pressed ? 10 : handleMouseArea.containsMouse ? 14 : 12
|
||||
color: Looks.colors.accent
|
||||
|
||||
Behavior on diameter {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
WToolTip {
|
||||
id: tooltip
|
||||
extraVisibleCondition: root.pressed
|
||||
text: root.tooltipContent
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
verticalPadding: 3
|
||||
horizontalPadding: 8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StackView {
|
||||
id: root
|
||||
property real moveDistance: 30
|
||||
property int pushDuration: 200
|
||||
property int fadeDuration: 80
|
||||
property list<real> bezierCurve: Looks.transition.easing.bezierCurve.easeIn
|
||||
property list<real> fadeBezierCurve: Looks.transition.easing.bezierCurve.easeInOut
|
||||
clip: true
|
||||
|
||||
background: null
|
||||
|
||||
pushEnter: Transition {
|
||||
XAnimator {
|
||||
from: -root.moveDistance
|
||||
to: 0
|
||||
duration: root.pushDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.bezierCurve
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: root.fadeDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.fadeBezierCurve
|
||||
}
|
||||
}
|
||||
pushExit: Transition {
|
||||
XAnimator {
|
||||
from: 0
|
||||
to: root.moveDistance
|
||||
duration: root.pushDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.bezierCurve
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: root.fadeDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.fadeBezierCurve
|
||||
}
|
||||
}
|
||||
popEnter: Transition {
|
||||
XAnimator {
|
||||
from: root.moveDistance
|
||||
to: 0
|
||||
duration: root.pushDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.bezierCurve
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity"
|
||||
from: 0
|
||||
to: 1
|
||||
duration: root.fadeDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.fadeBezierCurve
|
||||
}
|
||||
}
|
||||
popExit: Transition {
|
||||
XAnimator {
|
||||
from: 0
|
||||
to: -root.moveDistance
|
||||
duration: root.pushDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.bezierCurve
|
||||
}
|
||||
NumberAnimation {
|
||||
properties: "opacity"
|
||||
from: 1
|
||||
to: 0
|
||||
duration: root.fadeDuration
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: root.fadeBezierCurve
|
||||
}
|
||||
}
|
||||
}
|
||||
66
surfaces/quickshell/ii-base/modules/waffle/looks/WSwitch.qml
Normal file
66
surfaces/quickshell/ii-base/modules/waffle/looks/WSwitch.qml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Switch {
|
||||
id: root
|
||||
|
||||
implicitWidth: 40
|
||||
implicitHeight: 20
|
||||
property real indicatorHeight: 12
|
||||
property real indicatorPressedHeight: 14
|
||||
property real indicatorPressedWidth: 17
|
||||
property color checkedColor: Looks.colors.accent
|
||||
property color uncheckedColor: Looks.colors.bg1
|
||||
property color borderColor: Looks.colors.controlBgInactive
|
||||
|
||||
readonly property real indicatorPressedWidthDiff: indicatorPressedWidth - indicatorHeight
|
||||
|
||||
background: Rectangle {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
radius: height / 2
|
||||
color: root.checked ? root.checkedColor : root.uncheckedColor
|
||||
border.width: 1
|
||||
border.color: root.checked ? root.checkedColor : root.borderColor
|
||||
|
||||
Behavior on color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
Behavior on border.color {
|
||||
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
|
||||
}
|
||||
}
|
||||
|
||||
// Custom thumb styling
|
||||
indicator: Rectangle {
|
||||
implicitWidth: (root.pressed || root.down) ? root.indicatorPressedWidth : root.indicatorHeight
|
||||
implicitHeight: (root.pressed || root.down) ? root.indicatorPressedHeight : root.indicatorHeight
|
||||
radius: height / 2
|
||||
color: root.checked ? Looks.colors.accentFg : root.borderColor
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: {
|
||||
if (root.checked) {
|
||||
return 24 - (root.pressed || root.down ? root.indicatorPressedWidthDiff : 0);
|
||||
} else {
|
||||
return (root.pressed || root.down) ? 3 : (Config.options.waffles.tweaks.switchHandlePositionFix ? 4 : 3);
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
Behavior on implicitWidth {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
19
surfaces/quickshell/ii-base/modules/waffle/looks/WText.qml
Normal file
19
surfaces/quickshell/ii-base/modules/waffle/looks/WText.qml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
|
||||
Text {
|
||||
id: root
|
||||
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Looks.colors.fg
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferDefaultHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
variableAxes: Looks.font.variableAxes.ui
|
||||
}
|
||||
|
||||
linkColor: Looks.colors.link
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WButton {
|
||||
id: root
|
||||
implicitHeight: 40
|
||||
implicitWidth: contentItem.implicitWidth + 30
|
||||
color: "transparent"
|
||||
|
||||
contentItem: Item {
|
||||
id: contentItem
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: buttonText.implicitWidth
|
||||
|
||||
WText {
|
||||
id: buttonText
|
||||
anchors.centerIn: parent
|
||||
color: root.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
text: root.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import qs.modules.common
|
||||
import QtQuick
|
||||
import QtQuick.Controls.FluentWinUI3
|
||||
import QtQuick.Controls
|
||||
|
||||
TextField {
|
||||
id: root
|
||||
|
||||
clip: true
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Looks.colors.fg
|
||||
|
||||
palette {
|
||||
active: Looks.colors.accent
|
||||
}
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferDefaultHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.IBeamCursor
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
TextInput {
|
||||
id: root
|
||||
renderType: Text.NativeRendering
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: Looks.colors.fg
|
||||
|
||||
font {
|
||||
hintingPreference: Font.PreferFullHinting
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
|
||||
selectionColor: Looks.colors.selection
|
||||
selectedTextColor: Looks.colors.selectionFg
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string longestText
|
||||
property alias text: textItem.text
|
||||
property alias font: textItem.font
|
||||
property alias horizontalAlignment: textItem.horizontalAlignment
|
||||
property alias verticalAlignment: textItem.verticalAlignment
|
||||
property alias color: textItem.color
|
||||
|
||||
implicitWidth: longestTextMetrics.width
|
||||
implicitHeight: longestTextMetrics.height
|
||||
|
||||
TextMetrics {
|
||||
id: longestTextMetrics
|
||||
text: root.longestText
|
||||
font {
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.large
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
}
|
||||
|
||||
WText {
|
||||
id: textItem
|
||||
anchors.fill: parent
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StyledToolTip {
|
||||
id: root
|
||||
|
||||
required property Item realContentItem
|
||||
font {
|
||||
family: Looks.font.family.ui
|
||||
pixelSize: Looks.font.pixelSize.normal
|
||||
weight: Looks.font.weight.regular
|
||||
}
|
||||
realContentItem: WText {
|
||||
text: root.text
|
||||
font: root.font
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
|
||||
verticalPadding: 8
|
||||
horizontalPadding: 10
|
||||
|
||||
delay: 400
|
||||
|
||||
contentItem: WToolTipContent {
|
||||
id: tooltipContent
|
||||
realContentItem: root.realContentItem
|
||||
horizontalPadding: root.horizontalPadding
|
||||
verticalPadding: root.verticalPadding
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
Item {
|
||||
id: root
|
||||
anchors.centerIn: parent
|
||||
required property Item realContentItem
|
||||
property alias radius: realContent.radius
|
||||
property real verticalPadding: 8
|
||||
property real horizontalPadding: 10
|
||||
implicitWidth: realContent.implicitWidth + 2 * 2
|
||||
implicitHeight: realContent.implicitHeight + 2 * 2
|
||||
|
||||
WAmbientShadow {
|
||||
target: realContent
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: realContent
|
||||
z: 1
|
||||
anchors.centerIn: parent
|
||||
implicitWidth: root.realContentItem.implicitWidth + root.horizontalPadding * 2
|
||||
implicitHeight: root.realContentItem.implicitHeight + root.verticalPadding * 2
|
||||
color: Looks.colors.bg1Base
|
||||
radius: Looks.radius.medium
|
||||
|
||||
children: [root.realContentItem]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property real padding: 9
|
||||
property alias colBackground: background.color
|
||||
property alias spacing: toolbarLayout.spacing
|
||||
property alias radius: background.radius
|
||||
default property alias toolbarData: toolbarLayout.data
|
||||
|
||||
implicitWidth: background.implicitWidth
|
||||
implicitHeight: background.implicitHeight
|
||||
|
||||
Rectangle {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
implicitHeight: 50
|
||||
implicitWidth: toolbarLayout.implicitWidth + root.padding * 2
|
||||
radius: Looks.radius.large
|
||||
color: Looks.colors.bg0Base
|
||||
|
||||
border.width: 1
|
||||
border.color: Looks.colors.bg1Border
|
||||
|
||||
RowLayout {
|
||||
id: toolbarLayout
|
||||
spacing: 4
|
||||
anchors {
|
||||
fill: parent
|
||||
margins: root.padding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
WButton {
|
||||
implicitHeight: 32
|
||||
radius: Looks.radius.medium
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
WToolbarButton {
|
||||
id: root
|
||||
implicitWidth: height
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: root.icon.name
|
||||
implicitSize: 18
|
||||
color: root.fgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
|
||||
TabButton {
|
||||
id: root
|
||||
|
||||
implicitWidth: 38
|
||||
implicitHeight: 32
|
||||
padding: 0
|
||||
|
||||
background: null
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
icon: root.icon.name
|
||||
color: root.icon.color
|
||||
implicitSize: 18
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.modules.common
|
||||
|
||||
Rectangle {
|
||||
Layout.leftMargin: 4
|
||||
Layout.rightMargin: 4
|
||||
implicitHeight: 24
|
||||
implicitWidth: 1
|
||||
color: Looks.colors.bg0Border
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
|
||||
TabBar {
|
||||
id: root
|
||||
implicitHeight: 32
|
||||
|
||||
background: Rectangle {
|
||||
radius: Looks.radius.medium
|
||||
color: Looks.colors.bgPanelFooter
|
||||
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, 0.7)
|
||||
border.width: 1
|
||||
|
||||
// Indicator
|
||||
Rectangle {
|
||||
anchors {
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
leftMargin: root.currentIndex * (root.width / root.count)
|
||||
Behavior on leftMargin {
|
||||
animation: Looks.transition.resize.createObject(this)
|
||||
}
|
||||
}
|
||||
radius: Looks.radius.medium
|
||||
color: Looks.colors.bg2Base
|
||||
border.color: Looks.colors.bg0Border
|
||||
border.width: 1
|
||||
width: root.width / root.count
|
||||
|
||||
Rectangle {
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 1
|
||||
}
|
||||
implicitWidth: pressDetector.containsPress ? 16 : 12
|
||||
implicitHeight: 3
|
||||
radius: height / 2
|
||||
color: Looks.colors.accent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: pressDetector
|
||||
z: 9999
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Qt5Compat.GraphicalEffects
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
StyledImage {
|
||||
id: avatar
|
||||
Layout.alignment: Qt.AlignTop
|
||||
sourceSize: Qt.size(32, 32)
|
||||
source: Directories.userAvatarPathAccountsService
|
||||
fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2]
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Circle {
|
||||
diameter: avatar.height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQml
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
BodyRectangle {
|
||||
id: root
|
||||
|
||||
// State
|
||||
property bool collapsed
|
||||
|
||||
// Locale
|
||||
property var locale: Qt.locale(Config.options.calendar.locale)
|
||||
|
||||
implicitHeight: collapsed ? 0 : contentColumn.implicitHeight
|
||||
implicitWidth: contentColumn.implicitWidth
|
||||
|
||||
Behavior on implicitHeight {
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
|
||||
clip: true
|
||||
ColumnLayout {
|
||||
id: contentColumn
|
||||
spacing: 12
|
||||
CalendarHeader {
|
||||
Layout.topMargin: 10
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: 5
|
||||
Layout.rightMargin: 5
|
||||
spacing: 1
|
||||
DayOfWeekRow {
|
||||
Layout.fillWidth: true
|
||||
locale: root.locale
|
||||
spacing: calendarView.buttonSpacing
|
||||
implicitHeight: calendarView.buttonSize
|
||||
delegate: Item {
|
||||
id: dayOfWeekItem
|
||||
required property var model
|
||||
implicitHeight: calendarView.buttonSize
|
||||
implicitWidth: calendarView.buttonSize
|
||||
WText {
|
||||
anchors.centerIn: parent
|
||||
text: {
|
||||
var result = dayOfWeekItem.model.shortName;
|
||||
if (Config.options.waffles.calendar.force2CharDayOfWeek) result = result.substring(0,2);
|
||||
return result;
|
||||
}
|
||||
color: Looks.colors.fg
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
}
|
||||
}
|
||||
}
|
||||
CalendarView {
|
||||
id: calendarView
|
||||
locale: root.locale
|
||||
verticalPadding: 2
|
||||
buttonSize: 41 // ???
|
||||
buttonSpacing: 6
|
||||
buttonVerticalSpacing: 1
|
||||
Layout.fillWidth: true
|
||||
delegate: DayButton {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component DayButton: WButton {
|
||||
id: dayButton
|
||||
required property var model
|
||||
checked: model.today
|
||||
enabled: hovered || checked || model.month === calendarView.focusedMonth
|
||||
implicitWidth: calendarView.buttonSize
|
||||
implicitHeight: calendarView.buttonSize
|
||||
radius: height / 2
|
||||
|
||||
required property int index
|
||||
|
||||
contentItem: Item {
|
||||
WText {
|
||||
anchors.centerIn: parent
|
||||
text: dayButton.model.day
|
||||
color: dayButton.fgColor
|
||||
font.pixelSize: Looks.font.pixelSize.larger
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component CalendarHeader: RowLayout {
|
||||
Layout.leftMargin: 8
|
||||
Layout.rightMargin: 8
|
||||
spacing: 8
|
||||
|
||||
WBorderlessButton {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 34
|
||||
contentItem: Item {
|
||||
WText {
|
||||
anchors.fill: parent
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
text: Qt.locale().toString(calendarView.focusedDate, "MMMM yyyy")
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
font.weight: Looks.font.weight.strong
|
||||
}
|
||||
}
|
||||
}
|
||||
ScrollMonthButton {
|
||||
scrollDown: false
|
||||
}
|
||||
ScrollMonthButton {
|
||||
scrollDown: true
|
||||
}
|
||||
}
|
||||
|
||||
component ScrollMonthButton: WBorderlessButton {
|
||||
id: scrollMonthButton
|
||||
required property bool scrollDown
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
onClicked: {
|
||||
calendarView.scrollMonthsAndSnap(scrollDown ? 1 : -1);
|
||||
}
|
||||
implicitWidth: 32
|
||||
implicitHeight: 34
|
||||
|
||||
contentItem: FluentIcon {
|
||||
filled: true
|
||||
implicitSize: 12
|
||||
icon: scrollMonthButton.scrollDown ? "caret-down" : "caret-up"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
FooterRectangle {
|
||||
id: root
|
||||
|
||||
implicitWidth: 0
|
||||
property bool collapsed
|
||||
color: ColorUtils.transparentize(Looks.colors.bgPanelBody, collapsed ? 0 : 1)
|
||||
Behavior on color {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
topMargin: 12
|
||||
bottomMargin: 12
|
||||
}
|
||||
|
||||
WText {
|
||||
Layout.fillWidth: true
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: DateTime.collapsedCalendarFormat
|
||||
}
|
||||
|
||||
WBorderedButton {
|
||||
implicitWidth: 24
|
||||
implicitHeight: 24
|
||||
padding: 0
|
||||
onClicked: root.collapsed = !root.collapsed
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: 12
|
||||
icon: "chevron-down"
|
||||
rotation: root.collapsed ? 180 : 0
|
||||
Behavior on rotation {
|
||||
animation: Looks.transition.rotate.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
FooterRectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitWidth: 0
|
||||
color: Looks.colors.bgPanelBody
|
||||
|
||||
RowLayout {
|
||||
anchors {
|
||||
fill: parent
|
||||
leftMargin: 16
|
||||
rightMargin: 16
|
||||
topMargin: 12
|
||||
bottomMargin: 12
|
||||
}
|
||||
spacing: 0
|
||||
|
||||
SmallBorderedIconButton {
|
||||
visible: !TimerService.pomodoroRunning
|
||||
icon.name: "subtract"
|
||||
onClicked: Config.options.time.pomodoro.focus -= 300 // 5 mins
|
||||
}
|
||||
|
||||
WTextWithFixedWidth {
|
||||
visible: !TimerService.pomodoroRunning
|
||||
implicitWidth: 81
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
color: Looks.colors.subfg
|
||||
text: Translation.tr("%1 mins").arg(`<font color="${Looks.colors.fg.toString()}">${TimerService.focusTime / 60}</font>`)
|
||||
}
|
||||
|
||||
SmallBorderedIconButton {
|
||||
visible: !TimerService.pomodoroRunning
|
||||
icon.name: "add"
|
||||
onClicked: Config.options.time.pomodoro.focus += 300 // 5 mins
|
||||
}
|
||||
|
||||
WText {
|
||||
visible: TimerService.pomodoroRunning
|
||||
font.pixelSize: Looks.font.pixelSize.large
|
||||
text: Translation.tr("Focusing")
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
SmallBorderedIconAndTextButton {
|
||||
iconName: TimerService.pomodoroRunning ? "stop" : "play"
|
||||
text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus")
|
||||
|
||||
onClicked: {
|
||||
if (TimerService.pomodoroRunning) {
|
||||
TimerService.togglePomodoro();
|
||||
TimerService.resetPomodoro();
|
||||
} else {
|
||||
TimerService.togglePomodoro();
|
||||
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarRight", "toggle"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Qt.labs.synchronizer
|
||||
import qs
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WBarAttachedPanelContent {
|
||||
id: root
|
||||
|
||||
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
|
||||
revealFromSides: true
|
||||
revealFromLeft: false
|
||||
|
||||
property bool collapsed: false
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
id: contentLayout
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
top: parent.top
|
||||
bottom: parent.bottom
|
||||
}
|
||||
spacing: 12
|
||||
|
||||
Item {
|
||||
id: notificationArea
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: notificationPane.implicitWidth
|
||||
|
||||
WPane {
|
||||
id: notificationPane
|
||||
anchors {
|
||||
bottom: parent.bottom
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
}
|
||||
contentItem: NotificationPaneContent {
|
||||
implicitWidth: calendarColumnLayout.implicitWidth
|
||||
implicitHeight: {
|
||||
if (Notifications.list.length > 0) {
|
||||
return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2)
|
||||
}
|
||||
return 230;
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: enableTimer
|
||||
interval: Config.options.hacks.arbitraryRaceConditionDelay
|
||||
onTriggered: heightBehavior.enabled = true;
|
||||
}
|
||||
Behavior on implicitHeight {
|
||||
id: heightBehavior
|
||||
enabled: false
|
||||
Component.onCompleted: {
|
||||
enableTimer.restart();
|
||||
}
|
||||
animation: Looks.transition.enter.createObject(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WPane {
|
||||
id: calendarPane
|
||||
contentItem: WPanelPageColumn {
|
||||
id: calendarColumnLayout
|
||||
DateHeader {
|
||||
Layout.fillWidth: true
|
||||
Synchronizer on collapsed {
|
||||
property alias source: root.collapsed
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {
|
||||
visible: !root.collapsed
|
||||
}
|
||||
|
||||
CalendarWidget {
|
||||
Layout.fillWidth: true
|
||||
Synchronizer on collapsed {
|
||||
property alias source: root.collapsed
|
||||
}
|
||||
}
|
||||
|
||||
WPanelSeparator {}
|
||||
|
||||
FocusFooter {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import qs.services
|
||||
import qs.modules.common
|
||||
import qs.modules.common.widgets
|
||||
import qs.modules.common.functions
|
||||
import qs.modules.waffle.looks
|
||||
|
||||
WBorderlessButton {
|
||||
id: root
|
||||
Layout.fillWidth: false
|
||||
property real implicitSize: 16
|
||||
implicitWidth: implicitSize
|
||||
implicitHeight: implicitSize
|
||||
color: "transparent"
|
||||
colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1
|
||||
|
||||
Behavior on colForeground {
|
||||
animation: Looks.transition.color.createObject(this)
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
FluentIcon {
|
||||
anchors.centerIn: parent
|
||||
implicitSize: root.implicitSize
|
||||
icon: root.icon.name
|
||||
color: root.colForeground
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue