Evernight & Caelestia
This commit is contained in:
@@ -2,6 +2,7 @@ pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components.containers
|
||||
import qs.components.misc
|
||||
import qs.Modules
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import Quickshell.Io
|
||||
|
||||
@@ -0,0 +1,293 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
MouseArea {
|
||||
id: root
|
||||
|
||||
required property LazyLoader loader
|
||||
required property ShellScreen screen
|
||||
|
||||
property bool onClient
|
||||
|
||||
property real realBorderWidth: onClient ? (Hypr.options["general:border_size"] ?? 1) : 2
|
||||
property real realRounding: onClient ? (Hypr.options["decoration:rounding"] ?? 0) : 0
|
||||
|
||||
property real ssx
|
||||
property real ssy
|
||||
|
||||
property real sx: 0
|
||||
property real sy: 0
|
||||
property real ex: screen.width
|
||||
property real ey: screen.height
|
||||
|
||||
property real rsx: Math.min(sx, ex)
|
||||
property real rsy: Math.min(sy, ey)
|
||||
property real sw: Math.abs(sx - ex)
|
||||
property real sh: Math.abs(sy - ey)
|
||||
|
||||
property list<var> clients: {
|
||||
const mon = Hypr.monitorFor(screen);
|
||||
if (!mon)
|
||||
return [];
|
||||
|
||||
const special = mon.lastIpcObject.specialWorkspace;
|
||||
const wsId = special.name ? special.id : mon.activeWorkspace.id;
|
||||
|
||||
return Hypr.toplevels.values.filter(c => c.workspace?.id === wsId).sort((a, b) => {
|
||||
// Pinned first, then fullscreen, then floating, then any other
|
||||
const ac = a.lastIpcObject;
|
||||
const bc = b.lastIpcObject;
|
||||
return (bc.pinned - ac.pinned) || ((bc.fullscreen !== 0) - (ac.fullscreen !== 0)) || (bc.floating - ac.floating);
|
||||
});
|
||||
}
|
||||
|
||||
function checkClientRects(x: real, y: real): void {
|
||||
for (const client of clients) {
|
||||
if (!client)
|
||||
continue;
|
||||
|
||||
let {
|
||||
at: [cx, cy],
|
||||
size: [cw, ch]
|
||||
} = client.lastIpcObject;
|
||||
cx -= screen.x;
|
||||
cy -= screen.y;
|
||||
if (cx <= x && cy <= y && cx + cw >= x && cy + ch >= y) {
|
||||
onClient = true;
|
||||
sx = cx;
|
||||
sy = cy;
|
||||
ex = cx + cw;
|
||||
ey = cy + ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function save(): void {
|
||||
const tmpfile = Qt.resolvedUrl(`/tmp/caelestia-picker-${Quickshell.processId}-${Date.now()}.png`);
|
||||
CUtils.saveItem(screencopy, tmpfile, Qt.rect(Math.ceil(rsx), Math.ceil(rsy), Math.floor(sw), Math.floor(sh)), path => Quickshell.execDetached(["swappy", "-f", path]));
|
||||
closeAnim.start();
|
||||
}
|
||||
|
||||
onClientsChanged: checkClientRects(mouseX, mouseY)
|
||||
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.CrossCursor
|
||||
|
||||
Component.onCompleted: {
|
||||
Hypr.extras.refreshOptions();
|
||||
|
||||
// Break binding if frozen
|
||||
if (loader.freeze)
|
||||
clients = clients;
|
||||
|
||||
opacity = 1;
|
||||
|
||||
const c = clients[0];
|
||||
if (c) {
|
||||
const cx = c.lastIpcObject.at[0] - screen.x;
|
||||
const cy = c.lastIpcObject.at[1] - screen.y;
|
||||
onClient = true;
|
||||
sx = cx;
|
||||
sy = cy;
|
||||
ex = cx + c.lastIpcObject.size[0];
|
||||
ey = cy + c.lastIpcObject.size[1];
|
||||
} else {
|
||||
sx = screen.width / 2 - 100;
|
||||
sy = screen.height / 2 - 100;
|
||||
ex = screen.width / 2 + 100;
|
||||
ey = screen.height / 2 + 100;
|
||||
}
|
||||
}
|
||||
|
||||
onPressed: event => {
|
||||
ssx = event.x;
|
||||
ssy = event.y;
|
||||
}
|
||||
|
||||
onReleased: {
|
||||
if (closeAnim.running)
|
||||
return;
|
||||
|
||||
if (root.loader.freeze) {
|
||||
save();
|
||||
} else {
|
||||
overlay.visible = border.visible = false;
|
||||
screencopy.visible = false;
|
||||
screencopy.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
onPositionChanged: event => {
|
||||
const x = event.x;
|
||||
const y = event.y;
|
||||
|
||||
if (pressed) {
|
||||
onClient = false;
|
||||
sx = ssx;
|
||||
sy = ssy;
|
||||
ex = x;
|
||||
ey = y;
|
||||
} else {
|
||||
checkClientRects(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
focus: true
|
||||
Keys.onEscapePressed: closeAnim.start()
|
||||
|
||||
SequentialAnimation {
|
||||
id: closeAnim
|
||||
|
||||
PropertyAction {
|
||||
target: root.loader
|
||||
property: "closing"
|
||||
value: true
|
||||
}
|
||||
ParallelAnimation {
|
||||
Anim {
|
||||
target: root
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
ExAnim {
|
||||
target: root
|
||||
properties: "rsx,rsy"
|
||||
to: 0
|
||||
}
|
||||
ExAnim {
|
||||
target: root
|
||||
property: "sw"
|
||||
to: root.screen.width
|
||||
}
|
||||
ExAnim {
|
||||
target: root
|
||||
property: "sh"
|
||||
to: root.screen.height
|
||||
}
|
||||
}
|
||||
PropertyAction {
|
||||
target: root.loader
|
||||
property: "activeAsync"
|
||||
value: false
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: screencopy
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
active: root.loader.freeze
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: ScreencopyView {
|
||||
captureSource: root.screen
|
||||
|
||||
onHasContentChanged: {
|
||||
if (hasContent && !root.loader.freeze) {
|
||||
overlay.visible = border.visible = true;
|
||||
root.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
id: overlay
|
||||
|
||||
anchors.fill: parent
|
||||
color: Colours.palette.m3secondaryContainer
|
||||
opacity: 0.3
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: MultiEffect {
|
||||
maskSource: selectionWrapper
|
||||
maskEnabled: true
|
||||
maskInverted: true
|
||||
maskSpreadAtMin: 1
|
||||
maskThresholdMin: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: selectionWrapper
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
visible: false
|
||||
|
||||
Rectangle {
|
||||
id: selectionRect
|
||||
|
||||
radius: root.realRounding
|
||||
x: root.rsx
|
||||
y: root.rsy
|
||||
implicitWidth: root.sw
|
||||
implicitHeight: root.sh
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: border
|
||||
|
||||
color: "transparent"
|
||||
radius: root.realRounding > 0 ? root.realRounding + root.realBorderWidth : 0
|
||||
border.width: root.realBorderWidth
|
||||
border.color: Colours.palette.m3primary
|
||||
|
||||
x: selectionRect.x - root.realBorderWidth
|
||||
y: selectionRect.y - root.realBorderWidth
|
||||
implicitWidth: selectionRect.implicitWidth + root.realBorderWidth * 2
|
||||
implicitHeight: selectionRect.implicitHeight + root.realBorderWidth * 2
|
||||
|
||||
Behavior on border.color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.large
|
||||
}
|
||||
}
|
||||
|
||||
Behavior on rsx {
|
||||
enabled: !root.pressed
|
||||
|
||||
ExAnim {}
|
||||
}
|
||||
|
||||
Behavior on rsy {
|
||||
enabled: !root.pressed
|
||||
|
||||
ExAnim {}
|
||||
}
|
||||
|
||||
Behavior on sw {
|
||||
enabled: !root.pressed
|
||||
|
||||
ExAnim {}
|
||||
}
|
||||
|
||||
Behavior on sh {
|
||||
enabled: !root.pressed
|
||||
|
||||
ExAnim {}
|
||||
}
|
||||
|
||||
component ExAnim: Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.containers
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Quickshell
|
||||
import Quickshell.Wayland
|
||||
import QtQuick
|
||||
|
||||
Loader {
|
||||
asynchronous: true
|
||||
active: Config.background.enabled
|
||||
|
||||
sourceComponent: Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
StyledWindow {
|
||||
id: win
|
||||
|
||||
required property ShellScreen modelData
|
||||
|
||||
screen: modelData
|
||||
name: "background"
|
||||
WlrLayershell.exclusionMode: ExclusionMode.Ignore
|
||||
WlrLayershell.layer: WlrLayer.Background
|
||||
color: "black"
|
||||
|
||||
anchors.top: true
|
||||
anchors.bottom: true
|
||||
anchors.left: true
|
||||
anchors.right: true
|
||||
|
||||
Wallpaper {
|
||||
id: wallpaper
|
||||
}
|
||||
|
||||
Loader {
|
||||
readonly property bool shouldBeActive: Config.background.visualiser.enabled && (!Config.background.visualiser.autoHide || Hypr.monitorFor(win.modelData).activeWorkspace.toplevels.values.every(t => t.lastIpcObject.floating)) ? 1 : 0
|
||||
property real offset: shouldBeActive ? 0 : win.modelData.height * 0.2
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.topMargin: offset
|
||||
anchors.bottomMargin: -offset
|
||||
opacity: shouldBeActive ? 1 : 0
|
||||
active: opacity > 0
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Visualiser {
|
||||
screen: win.modelData
|
||||
wallpaper: wallpaper
|
||||
}
|
||||
|
||||
Behavior on offset {
|
||||
Anim {}
|
||||
}
|
||||
|
||||
Behavior on opacity {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.right: parent.right
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.margins: Appearance.padding.large
|
||||
|
||||
active: Config.background.desktopClock.enabled
|
||||
asynchronous: true
|
||||
|
||||
source: "DesktopClock.qml"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
implicitWidth: timeText.implicitWidth + Appearance.padding.large * 2
|
||||
implicitHeight: timeText.implicitHeight + Appearance.padding.large * 2
|
||||
|
||||
StyledText {
|
||||
id: timeText
|
||||
|
||||
anchors.centerIn: parent
|
||||
text: Time.format(Config.services.useTwelveHourClock ? "hh:mm:ss A" : "hh:mm:ss")
|
||||
font.pointSize: Appearance.font.size.extraLarge
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.services
|
||||
import qs.config
|
||||
import Caelestia.Services
|
||||
import Quickshell
|
||||
import Quickshell.Widgets
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property Wallpaper wallpaper
|
||||
|
||||
ServiceRef {
|
||||
service: Audio.cava
|
||||
}
|
||||
|
||||
MultiEffect {
|
||||
anchors.fill: parent
|
||||
source: root.wallpaper
|
||||
maskSource: wrapper
|
||||
maskEnabled: true
|
||||
blurEnabled: true
|
||||
blur: 1
|
||||
blurMax: 32
|
||||
autoPaddingEnabled: false
|
||||
}
|
||||
|
||||
Item {
|
||||
id: wrapper
|
||||
|
||||
anchors.fill: parent
|
||||
layer.enabled: true
|
||||
|
||||
Item {
|
||||
id: content
|
||||
|
||||
anchors.fill: parent
|
||||
anchors.margins: Config.border.thickness
|
||||
anchors.leftMargin: Visibilities.bars.get(root.screen).exclusiveZone + Appearance.spacing.small * Config.background.visualiser.spacing
|
||||
|
||||
Side {}
|
||||
Side {
|
||||
isRight: true
|
||||
}
|
||||
|
||||
Behavior on anchors.leftMargin {
|
||||
Anim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
component Side: Repeater {
|
||||
id: side
|
||||
|
||||
property bool isRight
|
||||
|
||||
model: Config.services.visualiserBars
|
||||
|
||||
ClippingRectangle {
|
||||
id: bar
|
||||
|
||||
required property int modelData
|
||||
property real value: Math.max(0, Math.min(1, Audio.cava.values[side.isRight ? modelData : side.count - modelData - 1]))
|
||||
|
||||
clip: true
|
||||
|
||||
x: modelData * ((content.width * 0.4) / Config.services.visualiserBars) + (side.isRight ? content.width * 0.6 : 0)
|
||||
implicitWidth: (content.width * 0.4) / Config.services.visualiserBars - Appearance.spacing.small * Config.background.visualiser.spacing
|
||||
|
||||
y: content.height - height
|
||||
implicitHeight: bar.value * content.height * 0.4
|
||||
|
||||
color: "transparent"
|
||||
topLeftRadius: Appearance.rounding.small * Config.background.visualiser.rounding
|
||||
topRightRadius: Appearance.rounding.small * Config.background.visualiser.rounding
|
||||
|
||||
Rectangle {
|
||||
topLeftRadius: parent.topLeftRadius
|
||||
topRightRadius: parent.topRightRadius
|
||||
|
||||
gradient: Gradient {
|
||||
orientation: Gradient.Vertical
|
||||
|
||||
GradientStop {
|
||||
position: 0
|
||||
color: Qt.alpha(Colours.palette.m3primary, 0.7)
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
GradientStop {
|
||||
position: 1
|
||||
color: Qt.alpha(Colours.palette.m3inversePrimary, 0.7)
|
||||
|
||||
Behavior on color {
|
||||
CAnim {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
y: parent.height - height
|
||||
implicitHeight: content.height * 0.4
|
||||
}
|
||||
|
||||
Behavior on value {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.small
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import qs.components
|
||||
import qs.components.images
|
||||
import qs.components.filedialog
|
||||
import qs.services
|
||||
import qs.config
|
||||
import qs.utils
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property string source: Wallpapers.current
|
||||
property Image current: one
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
onSourceChanged: {
|
||||
if (!source)
|
||||
current = null;
|
||||
else if (current === one)
|
||||
two.update();
|
||||
else
|
||||
one.update();
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
|
||||
active: !root.source
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: StyledRect {
|
||||
color: Colours.palette.m3surfaceContainer
|
||||
|
||||
Row {
|
||||
anchors.centerIn: parent
|
||||
spacing: Appearance.spacing.large
|
||||
|
||||
MaterialIcon {
|
||||
text: "sentiment_stressed"
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge * 5
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Appearance.spacing.small
|
||||
|
||||
StyledText {
|
||||
text: qsTr("Wallpaper missing?")
|
||||
color: Colours.palette.m3onSurfaceVariant
|
||||
font.pointSize: Appearance.font.size.extraLarge * 2
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
StyledRect {
|
||||
implicitWidth: selectWallText.implicitWidth + Appearance.padding.large * 2
|
||||
implicitHeight: selectWallText.implicitHeight + Appearance.padding.small * 2
|
||||
|
||||
radius: Appearance.rounding.full
|
||||
color: Colours.palette.m3primary
|
||||
|
||||
FileDialog {
|
||||
id: dialog
|
||||
|
||||
title: qsTr("Select a wallpaper")
|
||||
filterLabel: qsTr("Image files")
|
||||
filters: Images.validImageExtensions
|
||||
onAccepted: path => Wallpapers.setWallpaper(path)
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
radius: parent.radius
|
||||
color: Colours.palette.m3onPrimary
|
||||
|
||||
function onClicked(): void {
|
||||
dialog.open();
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: selectWallText
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: qsTr("Set it now!")
|
||||
color: Colours.palette.m3onPrimary
|
||||
font.pointSize: Appearance.font.size.large
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Img {
|
||||
id: one
|
||||
}
|
||||
|
||||
Img {
|
||||
id: two
|
||||
}
|
||||
|
||||
component Img: CachingImage {
|
||||
id: img
|
||||
|
||||
function update(): void {
|
||||
if (path === root.source)
|
||||
root.current = this;
|
||||
else
|
||||
path = root.source;
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
opacity: 0
|
||||
scale: Wallpapers.showPreview ? 1 : 0.8
|
||||
|
||||
onStatusChanged: {
|
||||
if (status === Image.Ready)
|
||||
root.current = this;
|
||||
}
|
||||
|
||||
states: State {
|
||||
name: "visible"
|
||||
when: root.current === img
|
||||
|
||||
PropertyChanges {
|
||||
img.opacity: 1
|
||||
img.scale: 1
|
||||
}
|
||||
}
|
||||
|
||||
transitions: Transition {
|
||||
Anim {
|
||||
target: img
|
||||
properties: "opacity,scale"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user