clipboard history using cliphist
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
id: root
|
||||
|
||||
enum ButtonType {
|
||||
Filled,
|
||||
Tonal,
|
||||
Text
|
||||
}
|
||||
|
||||
property color activeColor
|
||||
property color activeOnColor
|
||||
property bool checked
|
||||
property real checkedRadius: Appearance.rounding.medium
|
||||
property real defaultRadius: Appearance.rounding.large
|
||||
property color disabledColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
|
||||
property color disabledOnColor: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
|
||||
property bool fillWidth
|
||||
property real horizontalPadding: padding
|
||||
readonly property alias hovered: stateLayer.containsMouse
|
||||
required implicitHeight
|
||||
required implicitWidth
|
||||
property color inactiveColor
|
||||
property color inactiveOnColor
|
||||
property bool internalChecked
|
||||
property bool isRound
|
||||
property bool isToggle
|
||||
readonly property color onColor: !enabled ? disabledOnColor : internalChecked ? activeOnColor : inactiveOnColor
|
||||
property real padding
|
||||
readonly property alias pressed: stateLayer.pressed
|
||||
property real pressedRadius: Appearance.rounding.small
|
||||
readonly property alias radiusAnim: radiusAnim
|
||||
property bool radiusMorph: true
|
||||
property alias shapeMorph: stateLayer.shapeMorph
|
||||
property real shapeMorphExpansion: shapeMorph && pressed ? 24 : 0
|
||||
readonly property alias stateLayer: stateLayer
|
||||
property int type: ButtonBase.Filled
|
||||
property real verticalPadding: padding
|
||||
|
||||
signal clicked
|
||||
|
||||
color: type === ButtonBase.Text ? "transparent" : !enabled ? disabledColor : internalChecked ? activeColor : inactiveColor
|
||||
radius: {
|
||||
if (radiusMorph && pressed)
|
||||
return pressedRadius;
|
||||
if (internalChecked)
|
||||
return checkedRadius;
|
||||
if (isRound)
|
||||
return (height || implicitHeight) / 2 * Math.min(1, Appearance.rounding.scale);
|
||||
return defaultRadius;
|
||||
}
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
id: radiusAnim
|
||||
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
Behavior on shapeMorphExpansion {
|
||||
Anim {
|
||||
type: Anim.FastSpatial
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: internalChecked = checked
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
color: root.internalChecked ? root.activeOnColor : root.inactiveOnColor
|
||||
enabled: enabled
|
||||
|
||||
onClicked: {
|
||||
if (root.isToggle)
|
||||
root.internalChecked = !root.internalChecked;
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-53
@@ -1,76 +1,45 @@
|
||||
import qs.Config
|
||||
import QtQuick
|
||||
import qs.Config
|
||||
|
||||
CustomRect {
|
||||
ButtonBase {
|
||||
id: root
|
||||
|
||||
enum Type {
|
||||
Filled,
|
||||
Tonal,
|
||||
Text
|
||||
}
|
||||
|
||||
property color activeColour: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
|
||||
property color activeOnColour: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
|
||||
property bool checked
|
||||
property color disabledColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.1)
|
||||
property color disabledOnColour: Qt.alpha(DynamicColors.palette.m3onSurface, 0.38)
|
||||
property alias font: label.font
|
||||
property alias icon: label.text
|
||||
property color inactiveColour: {
|
||||
if (!toggle && type === IconButton.Filled)
|
||||
readonly property alias label: label
|
||||
|
||||
activeColor: type === IconButton.Filled ? DynamicColors.palette.m3primary : DynamicColors.palette.m3secondary
|
||||
activeOnColor: type === IconButton.Filled ? DynamicColors.palette.m3onPrimary : type === IconButton.Tonal ? DynamicColors.palette.m3onSecondary : DynamicColors.palette.m3primary
|
||||
implicitHeight: {
|
||||
const h = label.implicitHeight + padding * 2;
|
||||
if (h % 2 !== 0)
|
||||
return h + 1;
|
||||
return h;
|
||||
}
|
||||
implicitWidth: implicitHeight
|
||||
inactiveColor: {
|
||||
if (!isToggle && type === IconButton.Filled)
|
||||
return DynamicColors.palette.m3primary;
|
||||
return type === IconButton.Filled ? DynamicColors.tPalette.m3surfaceContainer : DynamicColors.palette.m3secondaryContainer;
|
||||
}
|
||||
property color inactiveOnColour: {
|
||||
if (!toggle && type === IconButton.Filled)
|
||||
inactiveOnColor: {
|
||||
if (!isToggle && type === IconButton.Filled)
|
||||
return DynamicColors.palette.m3onPrimary;
|
||||
return type === IconButton.Tonal ? DynamicColors.palette.m3onSecondaryContainer : DynamicColors.palette.m3onSurfaceVariant;
|
||||
}
|
||||
property bool internalChecked
|
||||
property alias label: label
|
||||
property real padding: type === IconButton.Text ? 10 / 2 : 7
|
||||
property alias radiusAnim: radiusAnim
|
||||
property alias stateLayer: stateLayer
|
||||
property bool toggle
|
||||
property int type: IconButton.Filled
|
||||
|
||||
signal clicked
|
||||
|
||||
color: type === IconButton.Text ? "transparent" : !enabled ? disabledColour : internalChecked ? activeColour : inactiveColour
|
||||
implicitHeight: label.implicitHeight + padding * 2
|
||||
implicitWidth: implicitHeight
|
||||
radius: internalChecked ? 6 : (implicitHeight / 2 * Math.min(1, 1)) * Appearance.rounding.scale
|
||||
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
id: radiusAnim
|
||||
}
|
||||
}
|
||||
|
||||
onCheckedChanged: internalChecked = checked
|
||||
|
||||
StateLayer {
|
||||
id: stateLayer
|
||||
|
||||
color: root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
|
||||
onClicked: {
|
||||
if (root.toggle)
|
||||
root.internalChecked = !root.internalChecked;
|
||||
root.clicked();
|
||||
}
|
||||
}
|
||||
padding: type === IconButton.Text ? Appearance.padding.extraSmall / 2 : Appearance.padding.small
|
||||
|
||||
MaterialIcon {
|
||||
id: label
|
||||
|
||||
anchors.centerIn: parent
|
||||
color: !root.enabled ? root.disabledOnColour : root.internalChecked ? root.activeOnColour : root.inactiveOnColour
|
||||
fill: !root.toggle || root.internalChecked ? 1 : 0
|
||||
anchors.verticalCenterOffset: 1
|
||||
color: root.onColor
|
||||
fill: !root.isToggle || root.internalChecked ? 1 : 0
|
||||
|
||||
Behavior on fill {
|
||||
Anim {
|
||||
type: Anim.DefaultEffects
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ Item {
|
||||
const dragX = x - centroid.pressPosition.x;
|
||||
const dragY = y - centroid.pressPosition.y;
|
||||
|
||||
if (centroid.pressPosition.y >= root.screen.height - Config.barConfig.border && dragY < -200)
|
||||
if (centroid.pressPosition.y >= root.screen.height - Config.barConfig.border && centroid.pressPosition.x > root.screen.width / 5 && dragY < -200)
|
||||
root.visibilities.launcher = true;
|
||||
|
||||
if (root.singleGestureTriggered)
|
||||
@@ -90,7 +90,10 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight)
|
||||
if (centroid.pressPosition.y > root.screen.height - Config.barConfig.border && centroid.pressPosition.x < root.screen.width / 5 && dragY < -50)
|
||||
root.visibilities.clipboard = true;
|
||||
|
||||
if (!Config.dock.hoverToReveal && centroid.pressPosition.y > root.screen.height - root.bar.implicitHeight && centroid.pressPosition.x > root.screen.width / 5)
|
||||
if (dragY < -10) {
|
||||
root.visibilities.dock = true;
|
||||
root.singleGestureTriggered = true;
|
||||
|
||||
@@ -13,12 +13,14 @@ import qs.Modules.Resources as Resources
|
||||
import qs.Modules.Settings as Settings
|
||||
import qs.Modules.Drawing as Drawing
|
||||
import qs.Modules.Dock as Dock
|
||||
import qs.Modules.Clipboard as Clipboard
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item bar
|
||||
readonly property alias clipboard: clipboard
|
||||
readonly property alias dashboard: dashboard
|
||||
readonly property alias dashboardWrapper: dashboardWrapper
|
||||
readonly property alias dock: dock
|
||||
@@ -207,4 +209,13 @@ Item {
|
||||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
|
||||
Clipboard.Wrapper {
|
||||
id: clipboard
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
|
||||
+16
-1
@@ -40,6 +40,7 @@ CustomWindow {
|
||||
visibilities.settings = false;
|
||||
visibilities.resources = false;
|
||||
visibilities.dock = false;
|
||||
visibilities.clipboard = false;
|
||||
panels.popouts.hasCurrent = false;
|
||||
}
|
||||
onHasFullscreenChanged: {
|
||||
@@ -49,6 +50,7 @@ CustomWindow {
|
||||
visibilities.osd = false;
|
||||
visibilities.settings = false;
|
||||
visibilities.resources = false;
|
||||
visibilities.clipboard = false;
|
||||
visibilities.dock = false;
|
||||
panels.popouts.hasCurrent = false;
|
||||
}
|
||||
@@ -96,7 +98,7 @@ CustomWindow {
|
||||
HyprlandFocusGrab {
|
||||
id: focusGrab
|
||||
|
||||
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
|
||||
active: visibilities.dock || visibilities.resources || visibilities.launcher || visibilities.sidebar || visibilities.dashboard || visibilities.settings || visibilities.clipboard || (panels.popouts.hasCurrent && panels.popouts.currentName.startsWith("traymenu"))
|
||||
windows: [root]
|
||||
|
||||
onCleared: {
|
||||
@@ -106,6 +108,7 @@ CustomWindow {
|
||||
visibilities.osd = false;
|
||||
visibilities.settings = false;
|
||||
visibilities.resources = false;
|
||||
visibilities.clipboard = false;
|
||||
visibilities.dock = false;
|
||||
panels.popouts.hasCurrent = false;
|
||||
}
|
||||
@@ -115,6 +118,7 @@ CustomWindow {
|
||||
id: visibilities
|
||||
|
||||
property bool bar
|
||||
property bool clipboard
|
||||
property bool dashboard
|
||||
property bool dock
|
||||
property bool isDrawing
|
||||
@@ -303,6 +307,14 @@ CustomWindow {
|
||||
panel: panels.drawing
|
||||
radius: Appearance.rounding.normal
|
||||
}
|
||||
|
||||
PanelBg {
|
||||
id: clipboardBg
|
||||
|
||||
deformAmount: 0.03
|
||||
panel: panels.clipboard
|
||||
radius: 29
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
@@ -355,6 +367,9 @@ CustomWindow {
|
||||
screen: root.screen
|
||||
visibilities: visibilities
|
||||
|
||||
clipboard.transform: Matrix4x4 {
|
||||
matrix: clipboardBg.deformMatrix
|
||||
}
|
||||
dashboard.transform: Matrix4x4 {
|
||||
matrix: dashBg.deformMatrix
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
pragma Singleton
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import qs.Config
|
||||
import "../scripts/fuzzysort.js" as Fuzzy
|
||||
|
||||
Singleton {
|
||||
id: root
|
||||
|
||||
property string cliphistBinary: "cliphist"
|
||||
property list<string> entries: []
|
||||
property real pasteDelay: 0.05
|
||||
readonly property var preparedEntries: entries.map(a => ({
|
||||
name: Fuzzy.prepare(`${a.replace(/^\s*\S+\s+/, "")}`),
|
||||
entry: a
|
||||
}))
|
||||
property string pressPasteCommand: "ydotool key -d 1 29:1 47:1 47:0 29:0"
|
||||
property real scoreThreshold: 0.2
|
||||
|
||||
function copy(entry) {
|
||||
if (root.cliphistBinary.includes("cliphist"))
|
||||
Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy`]);
|
||||
else {
|
||||
const entryNumber = entry.split("\t")[0];
|
||||
Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy`]);
|
||||
}
|
||||
}
|
||||
|
||||
function deleteEntry(entry) {
|
||||
deleteProc.deleteEntry(entry);
|
||||
}
|
||||
|
||||
function entryIsImage(entry) {
|
||||
return !!(/^\d+\t\[\[.*binary data.*\d+x\d+.*\]\]$/.test(entry));
|
||||
}
|
||||
|
||||
function fuzzyQuery(search: string): var {
|
||||
if (search.trim() === "") {
|
||||
return entries;
|
||||
}
|
||||
return Fuzzy.go(search, preparedEntries, {
|
||||
all: true,
|
||||
key: "name"
|
||||
}).map(r => {
|
||||
return r.obj.entry;
|
||||
});
|
||||
}
|
||||
|
||||
function paste(entry) {
|
||||
if (root.cliphistBinary.includes("cliphist"))
|
||||
Quickshell.execDetached(["bash", "-c", `printf '${shellSingleQuoteEscape(entry)}' | ${root.cliphistBinary} decode | wl-copy && wl-paste`]);
|
||||
else {
|
||||
const entryNumber = entry.split("\t")[0];
|
||||
Quickshell.execDetached(["bash", "-c", `${root.cliphistBinary} decode ${entryNumber} | wl-copy; ${root.pressPasteCommand}`]);
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
readProc.buffer = [];
|
||||
readProc.running = true;
|
||||
}
|
||||
|
||||
function shellSingleQuoteEscape(str) {
|
||||
return String(str).replace(/'/g, "'\\''");
|
||||
}
|
||||
|
||||
function wipe() {
|
||||
wipeProc.running = true;
|
||||
}
|
||||
|
||||
Process {
|
||||
id: deleteProc
|
||||
|
||||
property string entry: ""
|
||||
|
||||
function deleteEntry(entry) {
|
||||
deleteProc.entry = entry;
|
||||
deleteProc.running = true;
|
||||
deleteProc.entry = "";
|
||||
}
|
||||
|
||||
command: ["bash", "-c", `echo '${root.shellSingleQuoteEscape(deleteProc.entry)}' | ${root.cliphistBinary} delete`]
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: wipeProc
|
||||
|
||||
command: [root.cliphistBinary, "wipe"]
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
root.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
function onClipboardTextChanged() {
|
||||
delayedUpdateTimer.restart();
|
||||
}
|
||||
|
||||
target: Quickshell
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: delayedUpdateTimer
|
||||
|
||||
interval: 50
|
||||
repeat: false
|
||||
|
||||
onTriggered: {
|
||||
root.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
Process {
|
||||
id: readProc
|
||||
|
||||
property list<string> buffer: []
|
||||
|
||||
command: [root.cliphistBinary, "list"]
|
||||
|
||||
stdout: SplitParser {
|
||||
onRead: line => {
|
||||
readProc.buffer.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
onExited: (exitCode, exitStatus) => {
|
||||
if (exitCode === 0) {
|
||||
root.entries = readProc.buffer;
|
||||
} else {
|
||||
console.error("[Cliphist] Failed to refresh with code", exitCode, "and status", exitStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
function update(): void {
|
||||
root.refresh();
|
||||
}
|
||||
|
||||
target: "cliphistService"
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,15 @@ Item {
|
||||
implicitWidth: content.implicitWidth
|
||||
visible: width > 0 && height > 0
|
||||
x: {
|
||||
if (content.isDetached)
|
||||
return (parent.width - content.nonAnimWidth) / 2;
|
||||
|
||||
const off = content.currentCenter - Config.barConfig.border - content.nonAnimWidth / 2;
|
||||
const diff = parent.width - Math.floor(off + content.nonAnimWidth);
|
||||
if (diff < 0)
|
||||
return off + diff;
|
||||
return Math.floor(Math.max(off, 0));
|
||||
}
|
||||
y: content.isDetached ? (parent.height - content.nonAnimHeight) / 2 : 0
|
||||
|
||||
Behavior on offsetScale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
Behavior on x {
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import qs.Components
|
||||
import qs.Helpers
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property ShellScreen screen
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
implicitHeight: screen.height / 2
|
||||
implicitWidth: 500
|
||||
|
||||
Component.onCompleted: {
|
||||
if (!ClipHistory.entries.length > 0)
|
||||
ClipHistory.refresh();
|
||||
searchField.forceActiveFocus();
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: search
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.tPalette.m3surfaceContainer
|
||||
implicitHeight: 50
|
||||
radius: Appearance.rounding.full
|
||||
|
||||
MaterialIcon {
|
||||
id: searchIcon
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.large
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: "search"
|
||||
}
|
||||
|
||||
CustomTextField {
|
||||
id: searchField
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: searchIcon.right
|
||||
anchors.leftMargin: Appearance.spacing.small
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
color: DynamicColors.palette.m3onSurface
|
||||
placeholderText: "Search clipboard history..."
|
||||
}
|
||||
}
|
||||
|
||||
CustomClippingRect {
|
||||
id: entries
|
||||
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: search.bottom
|
||||
anchors.topMargin: Appearance.spacing.normal
|
||||
radius: Appearance.rounding.normal
|
||||
|
||||
ListView {
|
||||
id: view
|
||||
|
||||
anchors.fill: parent
|
||||
spacing: Appearance.spacing.normal
|
||||
|
||||
delegate: RowLayout {
|
||||
id: clipItem
|
||||
|
||||
required property string modelData
|
||||
|
||||
height: 50
|
||||
width: view.width
|
||||
|
||||
CustomClippingRect {
|
||||
id: textRect
|
||||
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
// Layout.preferredWidth: implicitWidth + (textLayer.pressed ? 18 : 0)
|
||||
// implicitWidth: 250
|
||||
radius: textLayer.pressed ? (Appearance.rounding.small / 2) : Appearance.rounding.small
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
Behavior on radius {
|
||||
Anim {
|
||||
type: Anim.FastEffects
|
||||
}
|
||||
}
|
||||
|
||||
CustomText {
|
||||
id: text
|
||||
|
||||
anchors.left: parent.left
|
||||
anchors.margins: Appearance.padding.normal
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
elide: Text.ElideRight
|
||||
text: clipItem.modelData
|
||||
}
|
||||
|
||||
StateLayer {
|
||||
id: textLayer
|
||||
|
||||
onClicked: ClipHistory.copy(clipItem.modelData)
|
||||
}
|
||||
}
|
||||
|
||||
IconButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: Appearance.padding.smallest
|
||||
Layout.preferredWidth: height
|
||||
icon: "content_copy"
|
||||
isToggle: false
|
||||
// implicitWidth: 30
|
||||
}
|
||||
|
||||
IconButton {
|
||||
Layout.fillHeight: true
|
||||
Layout.margins: Appearance.padding.smallest
|
||||
Layout.preferredWidth: height
|
||||
icon: "delete"
|
||||
inactiveColor: Qt.alpha(DynamicColors.palette.m3error, 0.8)
|
||||
inactiveOnColor: DynamicColors.palette.m3onError
|
||||
isToggle: false
|
||||
}
|
||||
}
|
||||
model: ScriptModel {
|
||||
values: {
|
||||
const entries = ClipHistory.entries;
|
||||
const search = searchField.text;
|
||||
var regex = new RegExp(search, "i");
|
||||
|
||||
return entries.filter(n => regex.test(n));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import Quickshell
|
||||
import QtQuick
|
||||
import qs.Components
|
||||
import qs.Config
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property int contentHeight
|
||||
property real offsetScale: shouldBeActive ? 0 : 1
|
||||
required property ShellScreen screen
|
||||
readonly property bool shouldBeActive: visibilities.clipboard
|
||||
required property PersistentProperties visibilities
|
||||
|
||||
anchors.bottomMargin: (-implicitHeight - 5) * offsetScale
|
||||
implicitHeight: content.implicitHeight + Appearance.padding.normal * 2
|
||||
implicitWidth: content.implicitWidth + Appearance.padding.normal * 2 || 400
|
||||
opacity: 1 - offsetScale
|
||||
visible: offsetScale < 1
|
||||
|
||||
Behavior on offsetScale {
|
||||
Anim {
|
||||
duration: Appearance.anim.durations.expressiveDefaultSpatial
|
||||
easing.bezierCurve: Appearance.anim.curves.expressiveDefaultSpatial
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: content
|
||||
|
||||
active: root.shouldBeActive || root.visible
|
||||
anchors.centerIn: parent
|
||||
asynchronous: true
|
||||
|
||||
sourceComponent: Content {
|
||||
screen: root.screen
|
||||
visibilities: root.visibilities
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -267,8 +267,8 @@ CustomRect {
|
||||
checked: Recorder.paused
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: Recorder.paused ? "play_arrow" : "pause"
|
||||
isToggle: true
|
||||
label.animate: true
|
||||
toggle: true
|
||||
type: IconButton.Tonal
|
||||
|
||||
onClicked: {
|
||||
@@ -280,8 +280,8 @@ CustomRect {
|
||||
IconButton {
|
||||
font.pointSize: Appearance.font.size.large
|
||||
icon: "stop"
|
||||
inactiveColour: DynamicColors.palette.m3error
|
||||
inactiveOnColour: DynamicColors.palette.m3onError
|
||||
inactiveColor: DynamicColors.palette.m3error
|
||||
inactiveOnColor: DynamicColors.palette.m3onError
|
||||
|
||||
onClicked: Recorder.stop()
|
||||
}
|
||||
|
||||
@@ -101,11 +101,11 @@ CustomRect {
|
||||
component Toggle: IconButton {
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredWidth: implicitWidth + (stateLayer.pressed ? 18 : internalChecked ? 7 : 0)
|
||||
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
inactiveColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
isToggle: true
|
||||
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
|
||||
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
|
||||
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
toggle: true
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
Anim {
|
||||
|
||||
@@ -221,11 +221,11 @@ Item {
|
||||
font: Appearance.font.family.sans
|
||||
// icon: root.iconForId(modelData.entry.id)
|
||||
icon: root.labelForId(modelData.entry.id)
|
||||
inactiveColour: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
inactiveColor: DynamicColors.layer(DynamicColors.palette.m3surfaceContainerHighest, 2)
|
||||
isToggle: true
|
||||
radius: stateLayer.pressed ? 6 / 2 : internalChecked ? 6 : 8
|
||||
radiusAnim.duration: MaterialEasing.expressiveEffectsTime
|
||||
radiusAnim.easing.bezierCurve: MaterialEasing.expressiveEffects
|
||||
toggle: true
|
||||
visible: !["spacer", "upower", "dash", "audio"].some(prefix => modelData.entry.id.startsWith(prefix))
|
||||
|
||||
Behavior on Layout.preferredWidth {
|
||||
|
||||
@@ -59,4 +59,13 @@ Scope {
|
||||
visibilities.settings = !visibilities.settings;
|
||||
}
|
||||
}
|
||||
|
||||
CustomShortcut {
|
||||
name: "toggle-clipboard"
|
||||
|
||||
onPressed: {
|
||||
const visibilities = Visibilities.getForActive();
|
||||
visibilities.clipboard = !visibilities.clipboard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ Item {
|
||||
detachedMode = "";
|
||||
}
|
||||
|
||||
focus: hasCurrent
|
||||
implicitHeight: nonAnimHeight
|
||||
implicitWidth: nonAnimWidth
|
||||
|
||||
|
||||
Reference in New Issue
Block a user