261 lines
6.0 KiB
QML
261 lines
6.0 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import Quickshell
|
|
import QtQuick
|
|
import QtQuick.Controls
|
|
import QtQuick.Layouts
|
|
import qs.Components
|
|
import qs.Helpers
|
|
import qs.Config
|
|
|
|
Item {
|
|
id: root
|
|
|
|
readonly property int itemHeight: Config.clipboard.sizes.itemHeight
|
|
required property ShellScreen screen
|
|
required property PersistentProperties visibilities
|
|
|
|
implicitHeight: search.implicitHeight + entries.anchors.topMargin + ((view.spacing + itemHeight) * Config.clipboard.maxEntriesShown) - view.spacing
|
|
implicitWidth: Config.clipboard.sizes.width + 500
|
|
|
|
Component.onCompleted: {
|
|
if (ClipHistory.entries.length === 0)
|
|
ClipHistory.refresh();
|
|
searchField.forceActiveFocus();
|
|
}
|
|
|
|
CustomClippingRect {
|
|
id: search
|
|
|
|
anchors.left: parent.left
|
|
anchors.right: entries.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..."
|
|
|
|
Keys.onDownPressed: view.incrementCurrentIndex()
|
|
Keys.onUpPressed: view.decrementCurrentIndex()
|
|
onAccepted: {
|
|
ClipHistory.copy(view.currentItem.modelData);
|
|
root.visibilities.clipboard = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
CustomClippingRect {
|
|
id: preview
|
|
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: entries.right
|
|
anchors.leftMargin: Appearance.spacing.normal
|
|
anchors.right: parent.right
|
|
anchors.top: parent.top
|
|
color: DynamicColors.tPalette.m3surfaceContainer
|
|
radius: 25
|
|
|
|
CustomText {
|
|
anchors.left: parent.left
|
|
anchors.margins: Appearance.padding.large
|
|
anchors.top: parent.top
|
|
text: ClipHistory.previewText
|
|
textFormat: Text.PlainText
|
|
visible: !ClipHistory.previewIsImage
|
|
width: preview.width - Appearance.padding.large * 2
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
Image {
|
|
anchors.fill: parent
|
|
anchors.margins: Appearance.padding.large
|
|
asynchronous: true
|
|
cache: false
|
|
fillMode: Image.PreserveAspectFit
|
|
mipmap: true
|
|
retainWhileLoading: true
|
|
smooth: true
|
|
source: ClipHistory.previewImageSource
|
|
visible: ClipHistory.previewIsImage
|
|
}
|
|
}
|
|
|
|
CustomClippingRect {
|
|
id: entries
|
|
|
|
anchors.bottom: parent.bottom
|
|
anchors.left: parent.left
|
|
anchors.top: search.bottom
|
|
anchors.topMargin: Appearance.spacing.normal
|
|
implicitWidth: Config.clipboard.sizes.width
|
|
radius: Appearance.rounding.small
|
|
|
|
CustomListView {
|
|
id: view
|
|
|
|
anchors.fill: parent
|
|
highlightFollowsCurrentItem: false
|
|
highlightRangeMode: ListView.ApplyRange
|
|
preferredHighlightBegin: 0
|
|
preferredHighlightEnd: height
|
|
spacing: Appearance.spacing.normal
|
|
|
|
CustomScrollBar.vertical: CustomScrollBar {
|
|
flickable: view
|
|
}
|
|
delegate: RowLayout {
|
|
id: clipItem
|
|
|
|
readonly property bool isImage: ClipHistory.entryIsImage(modelData)
|
|
required property string modelData
|
|
|
|
height: root.itemHeight
|
|
spacing: Appearance.spacing.small
|
|
width: view.width
|
|
|
|
CustomClippingRect {
|
|
id: textRect
|
|
|
|
Layout.fillHeight: true
|
|
Layout.fillWidth: true
|
|
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
|
|
}
|
|
}
|
|
|
|
Item {
|
|
id: textWrapper
|
|
|
|
anchors.fill: parent
|
|
layer.enabled: true
|
|
|
|
layer.effect: OpacityMask {
|
|
maskSource: fadeMask
|
|
}
|
|
|
|
MaterialIcon {
|
|
id: icon
|
|
|
|
anchors.left: parent.left
|
|
anchors.margins: Appearance.padding.normal
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
font.pointSize: Appearance.font.size.large
|
|
text: clipItem.isImage ? "image" : "text_fields"
|
|
}
|
|
|
|
CustomText {
|
|
id: text
|
|
|
|
anchors.left: icon.right
|
|
anchors.margins: Appearance.spacing.normal
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
elide: Text.ElideRight
|
|
text: clipItem.isImage ? qsTr("Image") : ClipHistory.displayText(clipItem.modelData)
|
|
}
|
|
}
|
|
|
|
CustomRect {
|
|
id: fadeMask
|
|
|
|
anchors.fill: parent
|
|
layer.enabled: true
|
|
visible: false
|
|
|
|
gradient: Gradient {
|
|
orientation: Gradient.Horizontal
|
|
|
|
GradientStop {
|
|
color: Qt.rgba(1, 1, 1, 1.0)
|
|
position: 0.85
|
|
}
|
|
|
|
GradientStop {
|
|
color: Qt.rgba(1, 1, 1, 0)
|
|
position: 1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
StateLayer {
|
|
id: textLayer
|
|
|
|
onClicked: ClipHistory.copy(clipItem.modelData)
|
|
}
|
|
}
|
|
|
|
IconButton {
|
|
Layout.bottomMargin: Appearance.padding.normal
|
|
Layout.fillHeight: true
|
|
Layout.preferredWidth: height
|
|
Layout.topMargin: Appearance.padding.normal
|
|
icon: "content_copy"
|
|
isToggle: false
|
|
}
|
|
|
|
IconButton {
|
|
Layout.fillHeight: true
|
|
Layout.margins: Appearance.padding.normal
|
|
Layout.preferredWidth: height
|
|
icon: "delete"
|
|
inactiveColor: Qt.alpha(DynamicColors.palette.m3error, 0.8)
|
|
inactiveOnColor: DynamicColors.palette.m3onError
|
|
isToggle: false
|
|
}
|
|
}
|
|
highlight: CustomRect {
|
|
color: DynamicColors.palette.m3onSurface
|
|
implicitHeight: view.currentItem?.height ?? 0
|
|
implicitWidth: view.width
|
|
opacity: 0.08
|
|
radius: Appearance.rounding.small
|
|
y: view.currentItem?.y ?? 0
|
|
|
|
Behavior on y {
|
|
Anim {
|
|
duration: Appearance.anim.durations.small
|
|
easing.bezierCurve: Appearance.anim.curves.expressiveEffects
|
|
}
|
|
}
|
|
}
|
|
model: ScriptModel {
|
|
values: ClipHistory.fuzzyQuery(searchField.text)
|
|
}
|
|
|
|
onCurrentItemChanged: {
|
|
if (!currentItem)
|
|
return;
|
|
|
|
ClipHistory.currentEntry = currentItem.modelData;
|
|
ClipHistory.refreshPreview();
|
|
}
|
|
}
|
|
}
|
|
}
|