Triangles clip includes border radius

This commit is contained in:
2025-12-28 12:38:13 -08:00
parent 4e7f2d0a37
commit b9b05ccf4d

119
shell.qml
View File

@@ -7,7 +7,6 @@ import QtQuick.Shapes
import QtQuick.Controls import QtQuick.Controls
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Widgets import Quickshell.Widgets
PanelWindow { PanelWindow {
@@ -15,8 +14,8 @@ PanelWindow {
function hide() { function hide() {
visible = false visible = false
searchInput.text = ""
list.visible = false list.visible = false
searchInput.text = ""
} }
IpcHandler { IpcHandler {
@@ -24,9 +23,11 @@ PanelWindow {
function show() { function show() {
visible = true visible = true
list.visible = true list.visible = true
list.modelChanged
} }
function hide() { function hide() {
root.hide() root.hide()
searchInput.text = ""
} }
function toggle() { visible ? hide() : show() } function toggle() { visible ? hide() : show() }
} }
@@ -39,19 +40,24 @@ PanelWindow {
top: true top: true
bottom: true bottom: true
} }
margins {
top: {
if (ToplevelManager.activeToplevel?.fullscreen) {
return 0
}
return 36;
}
}
readonly property int radius: 20 readonly property int radius: 20
readonly property var font: { readonly property var font: {
family: "comfortaa" family: "comfortaa"
} }
readonly property int entry_height: 100 readonly property int entry_height: 100
property var currentApps: {
let apps = Array.from(DesktopEntries.applications.values);
if (searchInput.text.length === 0) {
apps.sort((a, b) => a.name.localeCompare(b.name));
return apps;
}
apps = apps.filter(app =>
app.name.toLowerCase().search(searchInput.text.toLowerCase()) !== -1
);
return apps
}
property string lastLaunched: ""
color: "transparent" color: "transparent"
Item { Item {
anchors.fill: parent anchors.fill: parent
@@ -68,33 +74,27 @@ PanelWindow {
break; break;
case Qt.Key_Return: case Qt.Key_Return:
list.currentItem.execute() list.currentItem.execute()
root.hide()
} }
} }
// TODO: Swap ListView for ScriptModel
ListView { ListView {
id: list id: list
spacing: 6 spacing: 6
anchors.fill: parent anchors.fill: parent
layer.enabled: true
highlightMoveDuration: 150 highlightMoveDuration: 150
highlightRangeMode: ListView.StrictlyEnforceRange highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 120 preferredHighlightBegin: 120
preferredHighlightEnd: this.height/2 + 200 preferredHighlightEnd: this.height/2
property var currentApps: { onModelChanged: {
let apps = Array.from(DesktopEntries.applications.values); if (searchInput.text === "")
apps.sort((a, b) => a.name.localeCompare(b.name)); currentIndex = currentApps.findIndex(entry =>
if (searchInput.text.length === 0) { entry.id === lastLaunched
return apps; );
} }
return apps.filter(app => {
return app.name.toLowerCase().search(searchInput.text.toLowerCase()) != -1
});
}
model: currentApps model: currentApps
delegate: Item { delegate: Button {
height: entry_height height: entry_height
width: root.width + 10 width: root.width + 10
property var leftMargin: (Math.pow(Math.abs(this.y - list.contentY - root.height/2), 1.8) / 1400) property var leftMargin: (Math.pow(Math.abs(this.y - list.contentY - root.height/2), 1.8) / 1400)
@@ -104,35 +104,29 @@ PanelWindow {
} }
z: -index z: -index
property real selectedOffset: ListView.isCurrentItem ? 16 : 64 property real selectedOffset: ListView.isCurrentItem ? 16 : 64
function execute() { modelData.execute() } function execute() {
modelData.execute()
root.lastLaunched = modelData.id
root.hide()
}
Behavior on selectedOffset { Behavior on selectedOffset {
NumberAnimation {duration: 500; easing.type: Easing.OutQuint} NumberAnimation {duration: 500; easing.type: Easing.OutQuint}
} }
Button {
id: button id: button
anchors { onClicked: execute()
fill: parent
// left: parent.left
// right: parent.right
// verticalCenter: parent.verticalCenter
}
onClicked: modelData.execute()
Component.onCompleted: {
}
// Calculate background color based on average color of icon // Calculate background color based on average color of icon
Canvas { Canvas {
id: canvas id: canvas
width: icon.width width: icon.width/2
height: icon.height height: icon.height/2
visible: false visible: false
onImageLoaded: { onImageLoaded: {
console.log("loaded");
requestPaint(); requestPaint();
} }
Component.onCompleted: loadImage(button.iconPath) Component.onCompleted: loadImage(button.iconPath)
} }
property color color: { property var color: {
if (!canvas.available) { if (!canvas.available) {
return null return null
} }
@@ -142,10 +136,13 @@ PanelWindow {
let avg = [0, 0, 0, 0]; let avg = [0, 0, 0, 0];
let count = 0; let count = 0;
for (let i = 0; i < pixels.length; i += 4) { for (let i = 0; i < pixels.length; i += 4) {
avg[0] += pixels[i+0]; let c_max = Math.max(pixels[i+0], pixels[i+1], pixels[i+2]);
avg[1] += pixels[i+1]; let c_min = Math.min(pixels[i+0], pixels[i+1], pixels[i+2]);
avg[2] += pixels[i+2]; let saturation = c_max === 0 ? 0 : (c_max - c_min) / c_max;
count += pixels[i+3]; avg[0] += pixels[i+0] * saturation;
avg[1] += pixels[i+1] * saturation;
avg[2] += pixels[i+2] * saturation;
count += pixels[i+3] * saturation;
} }
return Qt.rgba(avg[0]/count, avg[1]/count, avg[2]/count, 1); return Qt.rgba(avg[0]/count, avg[1]/count, avg[2]/count, 1);
} }
@@ -158,7 +155,7 @@ PanelWindow {
gradient: Gradient { gradient: Gradient {
orientation: Gradient.Horizontal orientation: Gradient.Horizontal
GradientStop { GradientStop {
color: Qt.darker(button.color, 16) color: Qt.darker(button.color, 8)
position: 0 position: 0
} }
GradientStop { GradientStop {
@@ -171,6 +168,7 @@ PanelWindow {
// Triangles // Triangles
Item { Item {
id: triangles id: triangles
visible: false
anchors.fill: parent anchors.fill: parent
clip: true clip: true
layer.enabled: true layer.enabled: true
@@ -187,7 +185,7 @@ PanelWindow {
y: (y_anim + y_off) % (entry_height + y_size) - y_size y: (y_anim + y_off) % (entry_height + y_size) - y_size
ShapePath { ShapePath {
strokeWidth: 2 strokeWidth: 2
strokeColor: Qt.darker(button.color, 3) strokeColor: Qt.darker(button.color, 2.5)
fillColor: "transparent" fillColor: "transparent"
startX: tri.x_size/2 startX: tri.x_size/2
@@ -198,14 +196,27 @@ PanelWindow {
} }
PropertyAnimation on y_anim { PropertyAnimation on y_anim {
from: entry_height + tri.y_size from: entry_height + tri.y_size
running: root.visible
to: 0 to: 0
duration: Math.random() * 12000 + 3500 duration: Math.random() * 12000 + 5000
loops: Animation.Infinite loops: Animation.Infinite
} }
} }
onObjectAdded: (index, obj) => obj.parent = triangles onObjectAdded: (index, obj) => obj.parent = triangles
} }
} }
MultiEffect {
source: triangles
anchors.fill: triangles
maskEnabled: true
maskSource: ShaderEffectSource { sourceItem: Rectangle {
x: triangles.x
y: triangles.y
width: triangles.width
height: triangles.height
radius: root.radius
} }
}
Item { Item {
anchors.fill: parent anchors.fill: parent
@@ -258,7 +269,6 @@ PanelWindow {
} }
} }
} }
}
Item { Item {
id: searchBox id: searchBox
@@ -315,6 +325,13 @@ PanelWindow {
opacity: 0.6 opacity: 0.6
} }
} }
Text {
text: "Search apps..."
anchors.fill: searchInput
font: searchInput.font
color: "#888"
visible: searchInput.text.length === 0
}
Image { Image {
source: Quickshell.iconPath("search") source: Quickshell.iconPath("search")
anchors { anchors {
@@ -325,14 +342,6 @@ PanelWindow {
width: 24 width: 24
height: 24 height: 24
} }
Text {
text: "Search apps..."
anchors.fill: searchInput
font: searchInput.font
color: "#888"
visible: searchInput.text.length === 0
} }
} }
}
} }