summaryrefslogtreecommitdiff
path: root/popups/launcher/components/IComponent.qml
blob: a237f49b10965e0fcf1e92d1522668f29f948974 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
import Quickshell
import QtQuick

import qs.components
import qs.config

IRect {
	id: root
	// virtual properties
	property bool standalone: false
	property var state: SelectionState
	property string name // Component/File name
	readonly property string _input: state.input
	readonly property string input: !standalone ? _input.slice(prefix.length) : _input
	readonly property string inputCleaned: input.toLowerCase().trim()
	property string prefix
	property bool active: !standalone ? input && _input.startsWith(prefix) : true
	property bool valid: !standalone ? processed?.valid || false : true
	property bool priority: !standalone ? valid && processed?.priority || false : true // please set priority to false if it's invalid
	property string answer: processed?.answer || ""
	property Component preview: Component {
		IText {
			// TODO: use proper active: false via Loader
			visible: answer.length <= Launcher.maxPreviewLen
			animate: true
			text: answer
		}
	}
	property string predictiveCompletion: processed?.predictiveCompletion || "" // would technically work with just from answer, but stuff like calc's answer wouldnt have anything to do with the input
	property var processed: active ? process() : {} // cached process, thought qml would do that automatically :/
	// todo: change all to function
	property var process: () => ({
				valid: valid,
				priority: priority,
				answer: answer,
				preview: preview,
				predictiveCompletion: predictiveCompletion
			})
	// use the current IRect if valid is set
	property var syncSelectionState: () => {}
	// actions
	property var up: () => ({
				top: true
			})
	property var down: () => ({
				bottom: true
			})
	property var prev: () => ({
				left: true
			})
	property var next: () => ({
				right: true
			})
	// true if not closed
	property var exec: () => {}
	property var home: () => ({
				top: true
			})
	property var end: () => ({
				bottom: true
			})
	property var pgup: () => ({
				top: true
			})
	property var pgdn: () => ({
				bottom: true
			})

	function enter() {
		trySetSelfPriority();
		// root or null hmm
		state.selected = root;
	}

	signal close

	function _exec() {
		if (!exec())
			close();
	}

	function getSelfPriority() {
		return state.priorities.indexOf(root);
	}

	function isSelectedPriority() {
		return state.selectedPriority === getSelfPriority();
	}

	function trySetSelfPriority() {
		let self = getSelfPriority();
		// ??? why tf is it -1 not null or undefined this is why rust is better
		if (self !== -1)
			state.selectedPriority = self;
	}

	function syncPriority() {
		if (!standalone) {
			if (state.priorities.includes(root) && !priority)
				state.priorities = state.priorities.filter(x => x !== root);
			else if (priority)
				state.priorities = [...new Set(state.priorities), root];
		} else
			state.priorities = [root];
	}

	// this doesn't change launcher size but only hides the actual content but the outside still think the content is there
	visible: active
	opacity: valid ? 1 : 0
	y: valid ? 0 : -Launcher.widgetHeight

	implicitWidth: valid ? Launcher.widgetWidth : 0
	implicitHeight: valid ? Launcher.widgetHeight : 0

	radius: Launcher.widgetRadius
	color: Qt.rgba(Colors.background3.r, Colors.background3.g, Colors.background3.b, Launcher.widgetBgTransparency) // TODO when prioritized, highlight

	onPriorityChanged: {
		// sync with state.priorities
		Qt.callLater(() => {
			syncPriority();
		});
	}

	Behavior on y {
		NumberAnimation {
			duration: General.animationDuration / 4
			easing.type: Easing.InOutQuad
		}
	}

	HoverHandler {
		onHoveredChanged: {
			if (hovered) {
				root.enter();
				root.syncSelectionState();
			}
		}
	}

	Component.onCompleted: {
		Qt.callLater(() => {
			syncPriority();
		});

		// Qt.callLater(() => {
		//     syncSelectionState();
		// });
	}

	Component.onDestruction: {
		state.priorities = state.priorities.filter(x => x !== root);
	}
}