feat: fullscreen notification & toasts overlay (#1276)
* reworked fullscreen mode as transforming shell * controlcenter configuring of toasts & notifs * fix animation on fs switch * border rounding & shadow animation * change controlcenter notifications layout * stay on WlrLayer.Overlay, use Anim * ci: update action versions * qmlformat * format take two * third time's the charm * fix: special workspace fullscreen * fix: bar width border.thickness on non-persistent behaviour * merge fix * stop layout shift on close * last few conventions sorted * linting * maximized state to fullscreen --------- Co-authored-by: 2 * r + 2 * t <61896496+soramanew@users.noreply.github.com>
This commit is contained in:
parent
b5f761666d
commit
c588794b20
24 changed files with 582 additions and 35 deletions
|
|
@ -55,6 +55,7 @@ Elevation {
|
|||
function onClicked(): void {
|
||||
root.itemSelected(item.modelData);
|
||||
root.active = item.modelData;
|
||||
item.modelData.clicked();
|
||||
root.expanded = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -267,11 +267,13 @@ Singleton {
|
|||
function serializeNotifs(): var {
|
||||
return {
|
||||
expire: notifs.expire,
|
||||
fullscreen: notifs.fullscreen,
|
||||
defaultExpireTimeout: notifs.defaultExpireTimeout,
|
||||
clearThreshold: notifs.clearThreshold,
|
||||
expandThreshold: notifs.expandThreshold,
|
||||
actionOnClick: notifs.actionOnClick,
|
||||
groupPreviewNum: notifs.groupPreviewNum
|
||||
groupPreviewNum: notifs.groupPreviewNum,
|
||||
openExpanded: notifs.openExpanded
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -323,6 +325,7 @@ Singleton {
|
|||
maxToasts: utilities.maxToasts,
|
||||
toasts: {
|
||||
configLoaded: utilities.toasts.configLoaded,
|
||||
fullscreen: utilities.toasts.fullscreen,
|
||||
chargingChanged: utilities.toasts.chargingChanged,
|
||||
gameModeChanged: utilities.toasts.gameModeChanged,
|
||||
dndChanged: utilities.toasts.dndChanged,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import Quickshell.Io
|
|||
|
||||
JsonObject {
|
||||
property bool expire: true
|
||||
property string fullscreen: "on"
|
||||
property int defaultExpireTimeout: 5000
|
||||
property real clearThreshold: 0.3
|
||||
property int expandThreshold: 20
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ JsonObject {
|
|||
|
||||
component Toasts: JsonObject {
|
||||
property bool configLoaded: true
|
||||
property string fullscreen: "off"
|
||||
property bool chargingChanged: true
|
||||
property bool gameModeChanged: true
|
||||
property bool dndChanged: true
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ Scope {
|
|||
id: root
|
||||
|
||||
property bool launcherInterrupted
|
||||
readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false
|
||||
readonly property bool hasFullscreen: Hypr.focusedWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false
|
||||
|
||||
// qmllint disable unresolved-type
|
||||
CustomShortcut {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ ColumnLayout {
|
|||
required property ShellScreen screen
|
||||
required property DrawerVisibilities visibilities
|
||||
required property BarPopouts.Wrapper popouts
|
||||
required property bool fullscreen
|
||||
readonly property int vPadding: Appearance.padding.large
|
||||
|
||||
function closeTray(): void {
|
||||
|
|
@ -128,6 +129,7 @@ ColumnLayout {
|
|||
delegate: WrappedLoader {
|
||||
sourceComponent: Workspaces {
|
||||
screen: root.screen
|
||||
fullscreen: root.fullscreen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -135,6 +137,7 @@ ColumnLayout {
|
|||
roleValue: "activeWindow"
|
||||
delegate: WrappedLoader {
|
||||
Layout.fillWidth: true
|
||||
visible: !root.fullscreen
|
||||
sourceComponent: ActiveWindow {
|
||||
bar: root
|
||||
monitor: Brightness.getMonitorForScreen(root.screen)
|
||||
|
|
@ -144,18 +147,21 @@ ColumnLayout {
|
|||
DelegateChoice {
|
||||
roleValue: "tray"
|
||||
delegate: WrappedLoader {
|
||||
visible: !root.fullscreen
|
||||
sourceComponent: Tray {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "clock"
|
||||
delegate: WrappedLoader {
|
||||
visible: !root.fullscreen
|
||||
sourceComponent: Clock {}
|
||||
}
|
||||
}
|
||||
DelegateChoice {
|
||||
roleValue: "statusIcons"
|
||||
delegate: WrappedLoader {
|
||||
visible: !root.fullscreen
|
||||
sourceComponent: StatusIcons {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,13 @@ Item {
|
|||
required property DrawerVisibilities visibilities
|
||||
required property BarPopouts.Wrapper popouts
|
||||
required property bool disabled
|
||||
required property bool fullscreen
|
||||
|
||||
readonly property int clampedWidth: Math.max(Config.border.minThickness, implicitWidth)
|
||||
readonly property int padding: Math.max(Appearance.padding.smaller, Config.border.thickness)
|
||||
readonly property int contentWidth: Config.bar.sizes.innerWidth + padding * 2
|
||||
readonly property int exclusiveZone: !disabled && (Config.bar.persistent || visibilities.bar) ? contentWidth : Config.border.thickness
|
||||
readonly property bool shouldBeVisible: !disabled && (Config.bar.persistent || visibilities.bar || isHovered)
|
||||
readonly property bool shouldBeVisible: !fullscreen && !disabled && (Config.bar.persistent || visibilities.bar || isHovered)
|
||||
property bool isHovered
|
||||
|
||||
function closeTray(): void {
|
||||
|
|
@ -33,8 +34,9 @@ Item {
|
|||
(content.item as Bar)?.handleWheel(y, angleDelta);
|
||||
}
|
||||
|
||||
visible: width > Config.border.thickness
|
||||
implicitWidth: Config.border.thickness
|
||||
clip: true
|
||||
visible: width > 0
|
||||
implicitWidth: fullscreen ? 0 : Config.border.thickness
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
|
|
@ -83,6 +85,7 @@ Item {
|
|||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
popouts: root.popouts // qmllint disable incompatible-type
|
||||
fullscreen: root.fullscreen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ StyledRect {
|
|||
required property int activeWsId
|
||||
required property Repeater workspaces
|
||||
required property Item mask
|
||||
required property bool fullscreen
|
||||
|
||||
readonly property int currentWsIdx: {
|
||||
let i = activeWsId - 1;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ StyledClippingRect {
|
|||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property bool fullscreen
|
||||
|
||||
readonly property bool onSpecial: (Config.bar.workspaces.perMonitorWorkspaces ? Hypr.monitorFor(screen) : Hypr.focusedMonitor)?.lastIpcObject.specialWorkspace?.name !== ""
|
||||
readonly property int activeWsId: Config.bar.workspaces.perMonitorWorkspaces ? (Hypr.monitorFor(screen).activeWorkspace?.id ?? 1) : Hypr.activeWsId
|
||||
|
|
@ -36,6 +37,7 @@ StyledClippingRect {
|
|||
anchors.fill: parent
|
||||
scale: root.onSpecial ? 0.8 : 1
|
||||
opacity: root.onSpecial ? 0.5 : 1
|
||||
visible: !root.fullscreen
|
||||
|
||||
layer.enabled: root.blur > 0
|
||||
layer.effect: MultiEffect {
|
||||
|
|
@ -86,6 +88,7 @@ StyledClippingRect {
|
|||
activeWsId: root.activeWsId
|
||||
workspaces: workspaces
|
||||
mask: layout
|
||||
fullscreen: root.fullscreen
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ QtObject {
|
|||
readonly property string icon: "task_alt"
|
||||
readonly property string component: "taskbar/TaskbarPane.qml"
|
||||
},
|
||||
QtObject {
|
||||
readonly property string id: "notifications"
|
||||
readonly property string label: "notifications"
|
||||
readonly property string icon: "notifications"
|
||||
readonly property string component: "notifications/NotificationsPane.qml"
|
||||
},
|
||||
QtObject {
|
||||
readonly property string id: "launcher"
|
||||
readonly property string label: "launcher"
|
||||
|
|
@ -60,7 +66,7 @@ QtObject {
|
|||
return result;
|
||||
}
|
||||
|
||||
function getByIndex(index: int): QtObject {
|
||||
function getByIndex(index: int): var {
|
||||
if (index >= 0 && index < panes.length) {
|
||||
return panes[index];
|
||||
}
|
||||
|
|
@ -76,12 +82,12 @@ QtObject {
|
|||
return -1;
|
||||
}
|
||||
|
||||
function getByLabel(label: string): QtObject {
|
||||
function getByLabel(label: string): var {
|
||||
const index = getIndexByLabel(label);
|
||||
return getByIndex(index);
|
||||
}
|
||||
|
||||
function getById(id: string): QtObject {
|
||||
function getById(id: string): var {
|
||||
for (let i = 0; i < panes.length; i++) {
|
||||
if (panes[i].id === id) {
|
||||
return panes[i];
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import "network"
|
|||
import "audio"
|
||||
import "appearance"
|
||||
import "taskbar"
|
||||
import "notifications"
|
||||
import "launcher"
|
||||
import "dashboard"
|
||||
import QtQuick
|
||||
|
|
|
|||
427
modules/controlcenter/notifications/NotificationsPane.qml
Normal file
427
modules/controlcenter/notifications/NotificationsPane.qml
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import ".."
|
||||
import "../components"
|
||||
import qs.components
|
||||
import qs.components.controls
|
||||
import qs.components.effects
|
||||
import qs.components.containers
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Session session
|
||||
|
||||
property bool notificationsExpire: Config.notifs.expire ?? true
|
||||
property string notificationsFullscreen: Config.notifs.fullscreen ?? "on"
|
||||
property bool notificationsOpenExpanded: Config.notifs.openExpanded ?? false
|
||||
property int notificationsDefaultExpireTimeout: Config.notifs.defaultExpireTimeout ?? 5000
|
||||
property int notificationsGroupPreviewNum: Config.notifs.groupPreviewNum ?? 3
|
||||
|
||||
property int maxToasts: Config.utilities.maxToasts ?? 4
|
||||
property string toastsFullscreen: Config.utilities.toasts.fullscreen ?? "off"
|
||||
property bool chargingChanged: Config.utilities.toasts.chargingChanged ?? true
|
||||
property bool gameModeChanged: Config.utilities.toasts.gameModeChanged ?? true
|
||||
property bool dndChanged: Config.utilities.toasts.dndChanged ?? true
|
||||
property bool audioOutputChanged: Config.utilities.toasts.audioOutputChanged ?? true
|
||||
property bool audioInputChanged: Config.utilities.toasts.audioInputChanged ?? true
|
||||
property bool capsLockChanged: Config.utilities.toasts.capsLockChanged ?? true
|
||||
property bool numLockChanged: Config.utilities.toasts.numLockChanged ?? true
|
||||
property bool kbLayoutChanged: Config.utilities.toasts.kbLayoutChanged ?? true
|
||||
property bool vpnChanged: Config.utilities.toasts.vpnChanged ?? true
|
||||
property bool nowPlaying: Config.utilities.toasts.nowPlaying ?? false
|
||||
|
||||
function saveConfig(): void {
|
||||
Config.notifs.expire = root.notificationsExpire;
|
||||
Config.notifs.fullscreen = root.notificationsFullscreen;
|
||||
Config.notifs.openExpanded = root.notificationsOpenExpanded;
|
||||
Config.notifs.defaultExpireTimeout = root.notificationsDefaultExpireTimeout;
|
||||
Config.notifs.groupPreviewNum = root.notificationsGroupPreviewNum;
|
||||
|
||||
Config.utilities.maxToasts = root.maxToasts;
|
||||
Config.utilities.toasts.fullscreen = root.toastsFullscreen;
|
||||
Config.utilities.toasts.chargingChanged = root.chargingChanged;
|
||||
Config.utilities.toasts.gameModeChanged = root.gameModeChanged;
|
||||
Config.utilities.toasts.dndChanged = root.dndChanged;
|
||||
Config.utilities.toasts.audioOutputChanged = root.audioOutputChanged;
|
||||
Config.utilities.toasts.audioInputChanged = root.audioInputChanged;
|
||||
Config.utilities.toasts.capsLockChanged = root.capsLockChanged;
|
||||
Config.utilities.toasts.numLockChanged = root.numLockChanged;
|
||||
Config.utilities.toasts.kbLayoutChanged = root.kbLayoutChanged;
|
||||
Config.utilities.toasts.vpnChanged = root.vpnChanged;
|
||||
Config.utilities.toasts.nowPlaying = root.nowPlaying;
|
||||
|
||||
Config.save();
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
ClippingRectangle {
|
||||
id: notificationsClippingRect
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.leftMargin: 0
|
||||
anchors.rightMargin: Appearance.padding.normal
|
||||
|
||||
color: "transparent"
|
||||
radius: notificationsBorder.innerRadius
|
||||
|
||||
Loader {
|
||||
id: notificationsLoader
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Appearance.padding.large + Appearance.padding.normal
|
||||
anchors.leftMargin: Appearance.padding.large
|
||||
anchors.rightMargin: Appearance.padding.large
|
||||
|
||||
sourceComponent: notificationsContentComponent
|
||||
}
|
||||
}
|
||||
|
||||
InnerBorder {
|
||||
id: notificationsBorder
|
||||
|
||||
leftThickness: 0
|
||||
rightThickness: Appearance.padding.normal
|
||||
}
|
||||
|
||||
Component {
|
||||
id: notificationsContentComponent
|
||||
|
||||
StyledFlickable {
|
||||
id: notificationsFlickable
|
||||
|
||||
flickableDirection: Flickable.VerticalFlick
|
||||
contentHeight: notificationsLayout.height
|
||||
|
||||
StyledScrollBar.vertical: StyledScrollBar {
|
||||
flickable: notificationsFlickable
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: notificationsLayout
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 500
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
SectionContainer {
|
||||
Layout.fillWidth: true
|
||||
alignTop: true
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Notifications")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
SplitButtonRow {
|
||||
id: notificationsFullscreenSelector
|
||||
|
||||
function syncActiveItem(): void {
|
||||
active = root.notificationsFullscreen === "off" ? notificationsFullscreenOffItem : notificationsFullscreenOnItem;
|
||||
}
|
||||
|
||||
label: qsTr("Show in fullscreen")
|
||||
menuItems: [notificationsFullscreenOffItem, notificationsFullscreenOnItem]
|
||||
|
||||
Component.onCompleted: syncActiveItem()
|
||||
|
||||
Connections {
|
||||
function onNotificationsFullscreenChanged(): void {
|
||||
notificationsFullscreenSelector.syncActiveItem();
|
||||
}
|
||||
|
||||
target: root
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: notificationsFullscreenOffItem
|
||||
|
||||
text: qsTr("Off")
|
||||
icon: "notifications_off"
|
||||
activeText: qsTr("Off")
|
||||
onClicked: {
|
||||
root.notificationsFullscreen = "off";
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: notificationsFullscreenOnItem
|
||||
|
||||
text: qsTr("On")
|
||||
icon: "notifications"
|
||||
activeText: qsTr("On")
|
||||
onClicked: {
|
||||
root.notificationsFullscreen = "on";
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
label: qsTr("Expire automatically")
|
||||
checked: root.notificationsExpire
|
||||
onToggled: checked => {
|
||||
root.notificationsExpire = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
label: qsTr("Open expanded")
|
||||
checked: root.notificationsOpenExpanded
|
||||
onToggled: checked => {
|
||||
root.notificationsOpenExpanded = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SpinBoxRow {
|
||||
label: qsTr("Default timeout")
|
||||
value: root.notificationsDefaultExpireTimeout
|
||||
min: 1000
|
||||
max: 60000
|
||||
step: 500
|
||||
onValueModified: value => {
|
||||
root.notificationsDefaultExpireTimeout = value;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SpinBoxRow {
|
||||
label: qsTr("Group preview count")
|
||||
value: root.notificationsGroupPreviewNum
|
||||
min: 1
|
||||
max: 10
|
||||
step: 1
|
||||
onValueModified: value => {
|
||||
root.notificationsGroupPreviewNum = value;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
SectionContainer {
|
||||
Layout.fillWidth: true
|
||||
alignTop: true
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Toast settings")
|
||||
font.pointSize: Appearance.font.size.normal
|
||||
}
|
||||
|
||||
SplitButtonRow {
|
||||
id: toastFullscreenSelector
|
||||
|
||||
function syncActiveItem(): void {
|
||||
if (root.toastsFullscreen === "all") {
|
||||
active = toastFullscreenAllItem;
|
||||
return;
|
||||
}
|
||||
|
||||
if (root.toastsFullscreen === "important") {
|
||||
active = toastFullscreenImportantItem;
|
||||
return;
|
||||
}
|
||||
|
||||
active = toastFullscreenOffItem;
|
||||
}
|
||||
|
||||
Layout.fillWidth: true
|
||||
z: expanded ? 100 : 0
|
||||
label: qsTr("Show in fullscreen")
|
||||
menuItems: [toastFullscreenOffItem, toastFullscreenImportantItem, toastFullscreenAllItem]
|
||||
|
||||
Component.onCompleted: syncActiveItem()
|
||||
|
||||
Connections {
|
||||
function onToastsFullscreenChanged(): void {
|
||||
toastFullscreenSelector.syncActiveItem();
|
||||
}
|
||||
|
||||
target: root
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: toastFullscreenOffItem
|
||||
|
||||
text: qsTr("Off")
|
||||
icon: "notifications_off"
|
||||
activeText: qsTr("Off")
|
||||
onClicked: {
|
||||
root.toastsFullscreen = "off";
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: toastFullscreenImportantItem
|
||||
|
||||
text: qsTr("Important")
|
||||
icon: "priority_high"
|
||||
activeText: qsTr("Important")
|
||||
onClicked: {
|
||||
root.toastsFullscreen = "important";
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
id: toastFullscreenAllItem
|
||||
|
||||
text: qsTr("On")
|
||||
icon: "notifications"
|
||||
activeText: qsTr("On")
|
||||
onClicked: {
|
||||
root.toastsFullscreen = "all";
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpinBoxRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Visible toasts")
|
||||
value: root.maxToasts
|
||||
min: 1
|
||||
max: 10
|
||||
step: 1
|
||||
onValueModified: value => {
|
||||
root.maxToasts = value;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
Layout.fillWidth: true
|
||||
columns: 2
|
||||
columnSpacing: Appearance.spacing.normal
|
||||
rowSpacing: Appearance.spacing.normal
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Charging changes")
|
||||
checked: root.chargingChanged
|
||||
onToggled: checked => {
|
||||
root.chargingChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Game mode changes")
|
||||
checked: root.gameModeChanged
|
||||
onToggled: checked => {
|
||||
root.gameModeChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Do not disturb")
|
||||
checked: root.dndChanged
|
||||
onToggled: checked => {
|
||||
root.dndChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Audio output changes")
|
||||
checked: root.audioOutputChanged
|
||||
onToggled: checked => {
|
||||
root.audioOutputChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Audio input changes")
|
||||
checked: root.audioInputChanged
|
||||
onToggled: checked => {
|
||||
root.audioInputChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Caps lock changes")
|
||||
checked: root.capsLockChanged
|
||||
onToggled: checked => {
|
||||
root.capsLockChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Num lock changes")
|
||||
checked: root.numLockChanged
|
||||
onToggled: checked => {
|
||||
root.numLockChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Keyboard layout changes")
|
||||
checked: root.kbLayoutChanged
|
||||
onToggled: checked => {
|
||||
root.kbLayoutChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("VPN changes")
|
||||
checked: root.vpnChanged
|
||||
onToggled: checked => {
|
||||
root.vpnChanged = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
SwitchRow {
|
||||
Layout.fillWidth: true
|
||||
label: qsTr("Now playing")
|
||||
checked: root.nowPlaying
|
||||
onToggled: checked => {
|
||||
root.nowPlaying = checked;
|
||||
root.saveConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,14 +15,17 @@ Shape {
|
|||
|
||||
required property Panels panels
|
||||
required property Item bar
|
||||
required property real borderThickness
|
||||
required property real borderRounding
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.border.thickness
|
||||
anchors.margins: root.borderThickness
|
||||
anchors.leftMargin: bar.implicitWidth
|
||||
preferredRendererType: Shape.CurveRenderer
|
||||
|
||||
Osd.Background {
|
||||
wrapper: root.panels.osd // qmllint disable incompatible-type
|
||||
rounding: Config.border.rounding
|
||||
|
||||
startX: root.width - root.panels.session.width - root.panels.sidebar.width
|
||||
startY: (root.height - wrapper.height) / 2 - rounding
|
||||
|
|
@ -31,6 +34,7 @@ Shape {
|
|||
Notifications.Background {
|
||||
wrapper: root.panels.notifications // qmllint disable incompatible-type
|
||||
sidebar: sidebar
|
||||
rounding: Config.border.rounding
|
||||
|
||||
startX: root.width
|
||||
startY: 0
|
||||
|
|
@ -68,6 +72,7 @@ Shape {
|
|||
Utilities.Background {
|
||||
wrapper: root.panels.utilities // qmllint disable incompatible-type
|
||||
sidebar: sidebar
|
||||
rounding: root.borderRounding
|
||||
|
||||
startX: root.width
|
||||
startY: root.height
|
||||
|
|
@ -78,6 +83,7 @@ Shape {
|
|||
|
||||
wrapper: root.panels.sidebar // qmllint disable incompatible-type
|
||||
panels: root.panels
|
||||
rounding: root.borderRounding
|
||||
|
||||
startX: root.width
|
||||
startY: root.panels.notifications.height
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import QtQuick
|
|||
import QtQuick.Effects
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item bar
|
||||
required property real borderThickness
|
||||
required property real borderRounding
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
|
|
@ -36,9 +37,9 @@ Item {
|
|||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.border.thickness
|
||||
anchors.margins: root.borderThickness
|
||||
anchors.leftMargin: root.bar.implicitWidth
|
||||
radius: Config.border.rounding
|
||||
radius: root.borderRounding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,18 +25,34 @@ Variants {
|
|||
Exclusions {
|
||||
screen: scope.modelData
|
||||
bar: bar
|
||||
borderThickness: Config.border.thickness
|
||||
}
|
||||
|
||||
StyledWindow {
|
||||
id: win
|
||||
|
||||
readonly property bool hasFullscreen: Hypr.monitorFor(screen)?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen === 2) ?? false
|
||||
readonly property var monitor: Hypr.monitorFor(screen)
|
||||
readonly property bool hasSpecialWorkspace: (monitor?.lastIpcObject?.specialWorkspace?.name.length ?? 0) > 0
|
||||
readonly property bool hasFullscreen: {
|
||||
if (hasSpecialWorkspace) {
|
||||
const specialName = monitor?.lastIpcObject?.specialWorkspace?.name;
|
||||
if (!specialName)
|
||||
return false;
|
||||
const specialWs = Hypr.workspaces.values.find(ws => ws.name === specialName);
|
||||
return specialWs?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
|
||||
}
|
||||
return monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1) ?? false;
|
||||
}
|
||||
property real borderThickness: hasFullscreen ? 0 : Config.border.thickness
|
||||
readonly property real borderLayoutThickness: hasFullscreen ? 0 : Config.border.thickness
|
||||
property real borderRounding: hasFullscreen ? 0 : Config.border.rounding
|
||||
property real shadowOpacity: hasFullscreen ? 0 : 0.7
|
||||
readonly property int dragMaskPadding: {
|
||||
if (focusGrab.active || panels.popouts.isDetached)
|
||||
return 0;
|
||||
|
||||
const mon = Hypr.monitorFor(screen);
|
||||
if (mon?.lastIpcObject.specialWorkspace?.name || mon?.activeWorkspace?.lastIpcObject.windows > 0)
|
||||
if (mon?.lastIpcObject.specialWorkspace?.name || mon?.activeWorkspace.lastIpcObject.windows > 0)
|
||||
return 0;
|
||||
|
||||
const thresholds = [];
|
||||
|
|
@ -55,6 +71,7 @@ Variants {
|
|||
screen: scope.modelData
|
||||
name: "drawers"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: WlrLayer.Overlay
|
||||
WlrLayershell.keyboardFocus: visibilities.launcher || visibilities.session || panels.dashboard.needsKeyboard ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None
|
||||
|
||||
mask: Region {
|
||||
|
|
@ -72,6 +89,30 @@ Variants {
|
|||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
Behavior on borderThickness {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on borderRounding {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on shadowOpacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.type: Easing.BezierSpline
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Variants {
|
||||
id: regions
|
||||
|
||||
|
|
@ -81,7 +122,7 @@ Variants {
|
|||
required property Item modelData
|
||||
|
||||
x: modelData.x + bar.implicitWidth
|
||||
y: modelData.y + Config.border.thickness
|
||||
y: modelData.y + win.borderLayoutThickness
|
||||
width: modelData.width
|
||||
height: modelData.height
|
||||
intersection: Intersection.Subtract
|
||||
|
|
@ -120,16 +161,20 @@ Variants {
|
|||
layer.effect: MultiEffect {
|
||||
shadowEnabled: true
|
||||
blurMax: 15
|
||||
shadowColor: Qt.alpha(Colours.palette.m3shadow, 0.7)
|
||||
shadowColor: Qt.alpha(Colours.palette.m3shadow, Math.max(0, win.shadowOpacity))
|
||||
}
|
||||
|
||||
Border {
|
||||
bar: bar
|
||||
borderThickness: win.borderThickness
|
||||
borderRounding: win.borderRounding
|
||||
}
|
||||
|
||||
Backgrounds {
|
||||
panels: panels
|
||||
bar: bar
|
||||
borderThickness: win.borderThickness
|
||||
borderRounding: win.borderRounding
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -145,6 +190,8 @@ Variants {
|
|||
visibilities: visibilities
|
||||
panels: panels
|
||||
bar: bar
|
||||
borderThickness: win.borderLayoutThickness
|
||||
fullscreen: win.hasFullscreen
|
||||
|
||||
Panels {
|
||||
id: panels
|
||||
|
|
@ -152,6 +199,7 @@ Variants {
|
|||
screen: scope.modelData
|
||||
visibilities: visibilities
|
||||
bar: bar
|
||||
borderThickness: win.borderLayoutThickness
|
||||
}
|
||||
|
||||
BarWrapper {
|
||||
|
|
@ -165,6 +213,7 @@ Variants {
|
|||
popouts: panels.popouts
|
||||
|
||||
disabled: scope.barDisabled
|
||||
fullscreen: win.hasFullscreen
|
||||
|
||||
Component.onCompleted: Visibilities.bars.set(scope.modelData, this)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ pragma ComponentBehavior: Bound
|
|||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.components.containers
|
||||
import qs.config
|
||||
import qs.modules.bar as Bar
|
||||
|
||||
Scope {
|
||||
|
|
@ -11,6 +10,7 @@ Scope {
|
|||
|
||||
required property ShellScreen screen
|
||||
required property Bar.BarWrapper bar
|
||||
required property real borderThickness
|
||||
|
||||
ExclusionZone {
|
||||
anchors.left: true
|
||||
|
|
@ -32,7 +32,7 @@ Scope {
|
|||
component ExclusionZone: StyledWindow {
|
||||
screen: root.screen
|
||||
name: "border-exclusion"
|
||||
exclusiveZone: Config.border.thickness
|
||||
exclusiveZone: root.borderThickness
|
||||
mask: Region {}
|
||||
implicitWidth: 1
|
||||
implicitHeight: 1
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ CustomMouseArea {
|
|||
required property DrawerVisibilities visibilities
|
||||
required property Panels panels
|
||||
required property Bar.BarWrapper bar
|
||||
required property real borderThickness
|
||||
required property bool fullscreen
|
||||
|
||||
property point dragStart
|
||||
property bool dashboardShortcutActive
|
||||
|
|
@ -22,7 +24,7 @@ CustomMouseArea {
|
|||
property bool utilitiesShortcutActive
|
||||
|
||||
function withinPanelHeight(panel: Item, x: real, y: real): bool {
|
||||
const panelY = Config.border.thickness + panel.y;
|
||||
const panelY = root.borderThickness + panel.y;
|
||||
return y >= panelY - Config.border.rounding && y <= panelY + panel.height + Config.border.rounding;
|
||||
}
|
||||
|
||||
|
|
@ -48,13 +50,16 @@ CustomMouseArea {
|
|||
}
|
||||
|
||||
function onWheel(event: WheelEvent): void {
|
||||
if (fullscreen)
|
||||
return;
|
||||
if (event.x < bar.implicitWidth) {
|
||||
bar.handleWheel(event.y, event.angleDelta);
|
||||
}
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
acceptedButtons: fullscreen ? Qt.NoButton : Qt.AllButtons
|
||||
hoverEnabled: !fullscreen
|
||||
|
||||
onPressed: event => dragStart = Qt.point(event.x, event.y)
|
||||
onContainsMouseChanged: {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ Item {
|
|||
required property ShellScreen screen
|
||||
required property DrawerVisibilities visibilities
|
||||
required property Bar.BarWrapper bar
|
||||
required property real borderThickness
|
||||
|
||||
readonly property alias osd: osd
|
||||
readonly property alias notifications: notifications
|
||||
|
|
@ -31,9 +32,17 @@ Item {
|
|||
readonly property alias sidebar: sidebar
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.border.thickness
|
||||
anchors.margins: root.borderThickness
|
||||
anchors.leftMargin: bar.implicitWidth
|
||||
|
||||
Behavior on anchors.margins {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Osd.Wrapper {
|
||||
id: osd
|
||||
|
||||
|
|
@ -100,7 +109,7 @@ Item {
|
|||
if (isDetached)
|
||||
return (root.height - nonAnimHeight) / 2;
|
||||
|
||||
const off = currentCenter - Config.border.thickness - nonAnimHeight / 2;
|
||||
const off = currentCenter - root.borderThickness - nonAnimHeight / 2;
|
||||
const diff = root.height - Math.floor(off + nonAnimHeight);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ import QtQuick
|
|||
import QtQuick.Shapes
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var sidebar
|
||||
readonly property real rounding: Config.border.rounding
|
||||
required property real rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
|
||||
|
|
@ -31,14 +30,14 @@ ShapePath {
|
|||
relativeY: root.wrapper.height - root.roundingY * 2
|
||||
}
|
||||
PathArc {
|
||||
relativeX: root.sidebar.notifsRoundingX
|
||||
relativeX: root.rounding
|
||||
relativeY: root.roundingY
|
||||
radiusX: root.sidebar.notifsRoundingX
|
||||
radiusX: root.rounding
|
||||
radiusY: Math.min(root.rounding, root.wrapper.height)
|
||||
direction: PathArc.Counterclockwise
|
||||
}
|
||||
PathLine {
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding - root.sidebar.notifsRoundingX : root.wrapper.width
|
||||
relativeX: root.wrapper.height > 0 ? root.wrapper.width - root.rounding * 2 : root.wrapper.width
|
||||
relativeY: 0
|
||||
}
|
||||
PathArc {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ import QtQuick
|
|||
import QtQuick.Shapes
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
readonly property real rounding: Config.border.rounding
|
||||
required property real rounding
|
||||
readonly property bool flatten: wrapper.width < rounding * 2
|
||||
readonly property real roundingX: flatten ? wrapper.width / 2 : rounding
|
||||
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@ import QtQuick
|
|||
import QtQuick.Shapes
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var panels
|
||||
|
||||
readonly property real rounding: Config.border.rounding
|
||||
required property real rounding
|
||||
|
||||
readonly property real notifsWidthDiff: panels.notifications.width - wrapper.width
|
||||
readonly property real notifsRoundingX: panels.notifications.height > 0 && notifsWidthDiff < rounding * 2 ? notifsWidthDiff / 2 : rounding
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@ import QtQuick
|
|||
import QtQuick.Shapes
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
ShapePath {
|
||||
id: root
|
||||
|
||||
required property Wrapper wrapper
|
||||
required property var sidebar
|
||||
readonly property real rounding: Config.border.rounding
|
||||
required property real rounding
|
||||
readonly property bool flatten: wrapper.height < rounding * 2
|
||||
readonly property real roundingY: flatten ? wrapper.height / 2 : rounding
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import QtQuick
|
|||
import Quickshell
|
||||
import Caelestia
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
|
||||
Item {
|
||||
|
|
@ -12,6 +13,16 @@ Item {
|
|||
readonly property int spacing: Appearance.spacing.small
|
||||
property bool flag
|
||||
|
||||
function shouldShowToast(toast: Toast): bool {
|
||||
if (!Notifs.hasFullscreen())
|
||||
return true;
|
||||
if (Config.utilities.toasts.fullscreen === "all")
|
||||
return true;
|
||||
if (Config.utilities.toasts.fullscreen === "important")
|
||||
return toast.type === Toast.Warning || toast.type === Toast.Error;
|
||||
return false;
|
||||
}
|
||||
|
||||
implicitWidth: Config.utilities.sizes.toastWidth - Appearance.padding.normal * 2
|
||||
implicitHeight: {
|
||||
let h = -spacing;
|
||||
|
|
@ -31,6 +42,8 @@ Item {
|
|||
const toasts = [];
|
||||
let count = 0;
|
||||
for (const toast of Toaster.toasts) {
|
||||
if (!root.shouldShowToast(toast))
|
||||
continue;
|
||||
toasts.push(toast);
|
||||
if (!toast.closed) {
|
||||
count++;
|
||||
|
|
|
|||
|
|
@ -21,6 +21,22 @@ Singleton {
|
|||
|
||||
property bool loaded
|
||||
|
||||
function hasFullscreen(): bool {
|
||||
for (const monitor of Hypr.monitors.values) {
|
||||
if (monitor?.activeWorkspace?.toplevels.values.some(t => t.lastIpcObject.fullscreen > 1))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function shouldShowPopup(): bool {
|
||||
if (props.dnd || [...Visibilities.screens.values()].some(v => v.sidebar))
|
||||
return false;
|
||||
if (Config.notifs.fullscreen === "off" && hasFullscreen())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
onDndChanged: {
|
||||
if (!Config.utilities.toasts.dndChanged)
|
||||
return;
|
||||
|
|
@ -79,7 +95,7 @@ Singleton {
|
|||
notif.tracked = true;
|
||||
|
||||
const comp = notifComp.createObject(root, {
|
||||
popup: !props.dnd && ![...Visibilities.screens.values()].some(v => v.sidebar),
|
||||
popup: root.shouldShowPopup(),
|
||||
notification: notif
|
||||
});
|
||||
root.list = [comp, ...root.list];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue