Merge branch 'end-4:main' into fix/workflow-run-on-forks

This commit is contained in:
Manpreet Singh
2025-11-17 19:00:43 +05:30
committed by GitHub
113 changed files with 2465 additions and 631 deletions
+17 -6
View File
@@ -2,22 +2,33 @@
- Please, please, please, make multiple PRs if you have many features/fixes, and don't shove your personal changes along with the PR, including changed defaults
- We can accept features that we do not personally want, but in that case we will ask you to make it configurable/optionally loaded.
- If you want to start working on something big to contribute, it might be a good idea to ask first to not waste your effort (but if you've already done it for yourself, it doesn't hurt to submit).
- If you want to start working on something _big_ to contribute, it might be a good idea to ask first to not waste your effort (but if you've already done it for yourself, it doesn't hurt to submit).
# Code details
# Translations
## Contributing to i18n
See `dots/.config/quickshell/ii/translations/tools`
For contributing in translation (i18n) for Quickshell, see also `dots/.config/quickshell/ii/translations/tools`.
# Code
## Dynamic loading
- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`. One tip with `Loader`s is sometimes you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`.
- If something's not always necessary, especially when guarded by a config option to enable/disable, put it in a `Loader`
- Note that you will need to declare positioning properties (like `anchors`) in the `Loader`, not the `sourceComponent`
- When something that's to be dynamically loaded doesn't affect its parent layout, you can have a fading animation by using FadeLoader and set the `shown` prop instead of `active` and `visible`
## Practical concerns
- Make sure what you add does not require significant resources for a minor purpose or harm usability just for the sake of looking nice. The dotfiles must remain practical for daily driving.
- If there is something really fancy and impractical anyway, add a config option for it and make sure it's disabled by default.
- If there is something really fancy and impractical anyway, add a config option for it and make sure it's disabled by default (example: constantly rotating background clock)
## Style
- Spaces
- Space properties and children data into meaningful groups. (but of course, don't use 2+ blanks in a row)
- Put spaces between text and operators: `if (condition) { ... } else { ... }` instead of `if(condition){ ... }else{ ... }`
- As you can see, it's pretty easy to use lots of nesting. There's no hard limit, end-4 himself nests a lot too, but avoid/mitigate that:
- Prefer early return: Use something like `if (!condition) return; doStuff();` instead of `if (condition) { doStuff() }`
- If you feel it's a bother to refractor something into a new file, remember there's `component` to declare reusable components in the same file.
# Setting up
+1 -1
View File
@@ -81,7 +81,7 @@ Widget system: Quickshell | Support: Yes
|:---|:---------------|
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/ea0154a1-e984-4bb6-a424-23247cefe3c6" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/6eba0d57-2606-4cea-8993-e6f169e82e70" /> |
| Window management | Weeb power |
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/e77a7c96-1905-4126-a2a0-434f818825a2" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c8544e99-8881-477f-b83a-d6e35c0184a1" /> |
| <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/a7e6d29c-e82c-4934-98db-36c810ec999b" /> | <img width="1920" height="1080" alt="image" src="https://github.com/user-attachments/assets/c8544e99-8881-477f-b83a-d6e35c0184a1" /> |
### Other styles: Available at the end of the readme.
+10 -3
View File
@@ -1,10 +1,17 @@
/diagnose.result
/cache
# Ignore Python cache files
__pycache__/
*.py[cod]
dots/.config/quickshell/ii/.qmlls.ini
.update-lock
/dots/.config/quickshell/ii/.qmlls.ini
# exp-update
/.update-lock
# custom os-release
/os-release
#emacs backup
# Emacs auto backup file
*~
+1
View File
@@ -70,6 +70,7 @@ x git submodule status --recursive
e "Checking distro"
x _check_distro_id
x cat os-release
#x _check_distro
e "Checking variables"
+1 -1
View File
@@ -14,7 +14,7 @@ exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISP
exec-once = hyprpm reload
# Audio
exec-once = easyeffects --gapplication-service
exec-once = easyeffects --hide-window --service-mode
# Clipboard: history
# exec-once = wl-paste --watch cliphist store &
+2 -2
View File
@@ -24,8 +24,8 @@ bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
bindit = ,Super_R, global, quickshell:workspaceNumber # [hidden]
bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
bindd = Super, Tab, Toggle overview, global, quickshell:overviewToggle # [hidden] Toggle overview/launcher (alt)
bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # Toggle left sidebar
bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # [hidden] Toggle overview/launcher (alt)
bind = Super, A, global, quickshell:sidebarLeftToggle # Toggle left sidebar
bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden]
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden]
+2
View File
@@ -133,12 +133,14 @@ layerrule = blurpopups, quickshell:.*
layerrule = blur, quickshell:.*
layerrule = ignorealpha 0.79, quickshell:.*
layerrule = animation slide, quickshell:bar
layerrule = noanim, quickshell:actionCenter
layerrule = animation slide bottom, quickshell:cheatsheet
layerrule = animation slide bottom, quickshell:dock
layerrule = animation popin 120%, quickshell:screenCorners
layerrule = noanim, quickshell:lockWindowPusher
layerrule = animation fade, quickshell:notificationPopup
layerrule = noanim, quickshell:overlay
layerrule = ignorealpha 1, quickshell:overlay
layerrule = noanim, quickshell:overview
layerrule = animation slide bottom, quickshell:osk
layerrule = noanim, quickshell:polkit
@@ -1,5 +1,5 @@
The "search" and "task view" icons are from here, with modifications
The "start-here", "search" and "task view" icons are from here, with modifications
https://www.figma.com/community/file/1123040825921884189/windows-11
[Windows 11 by Joshua Oghenekaro Okwe - Figma](https://www.figma.com/community/file/1123040825921884189/windows-11)
License: CC BY 4.0
License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/deed.en)
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
version="1.1"
id="svg1"
sodipodi:docname="arrow-sync.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1" />
<sodipodi:namedview
id="namedview1"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="25.868323"
inkscape:cx="14.245222"
inkscape:cy="12.447657"
inkscape:window-width="1498"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1" />
<path
d="m 19.947024,13.17933 a 0.75,0.75 0 0 0 -0.907212,0.549312 7.251,7.251 0 0 1 -13.334866,1.873667 l 0.8908023,0.350894 a 0.75,0.75 0 0 0 0.6494514,-1.347747 l -0.100442,-0.04793 -2.6316174,-1.036623 a 0.75,0.75 0 0 0 -0.9241627,0.322228 l -0.047921,0.100457 -1.0366214,2.631619 a 0.75,0.75 0 0 0 1.3468323,0.649854 l 0.048321,-0.09951 0.3887326,-0.986859 A 8.75,8.75 0 0 0 20.494492,14.087329 0.75,0.75 0 0 0 19.946102,13.179722 Z M 21.068469,6.4543893 A 0.75,0.75 0 0 0 20.096388,6.8770607 L 19.707652,7.863919 A 8.75,8.75 0 0 0 3.5868012,9.5848274 0.75043107,0.75043107 0 1 0 5.0290446,10.000183 7.251,7.251 0 0 1 18.290631,8.3993655 l -0.8904,-0.3499798 a 0.75,0.75 0 1 0 -0.549014,1.3956769 l 2.631617,1.0366214 a 0.75,0.75 0 0 0 0.972088,-0.422667 L 21.491544,7.4273949 A 0.75,0.75 0 0 0 21.068874,6.455308 Z"
fill="#212121"
id="path1" />
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M4.293 8.293a1 1 0 0 1 1.414 0L12 14.586l6.293-6.293a1 1 0 1 1 1.414 1.414l-7 7a1 1 0 0 1-1.414 0l-7-7a1 1 0 0 1 0-1.414Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 249 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M3.28 2.22a.75.75 0 0 0-1.06 1.06l5.905 5.905L4.81 10.33a1.25 1.25 0 0 0-.476 2.065L7.439 15.5 3 19.94V21h1.06l4.44-4.44 3.105 3.105a1.25 1.25 0 0 0 2.065-.476l1.145-3.313 5.905 5.904a.75.75 0 0 0 1.06-1.06L3.28 2.22Zm10.355 12.476-1.252 3.626-6.705-6.705 3.626-1.252 4.331 4.331Zm6.048-3.876-3.787 1.894 1.118 1.118 3.34-1.67a2.75 2.75 0 0 0 .714-4.404l-4.825-4.826a2.75 2.75 0 0 0-4.405.715l-1.67 3.34 1.118 1.117 1.894-3.787a1.25 1.25 0 0 1 2.002-.325l4.826 4.826a1.25 1.25 0 0 1-.325 2.002Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 622 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m16.242 2.932 4.826 4.826a2.75 2.75 0 0 1-.715 4.404l-4.87 2.435a.75.75 0 0 0-.374.426l-1.44 4.166a1.25 1.25 0 0 1-2.065.476L8.5 16.561 4.06 21H3v-1.06l4.44-4.44-3.105-3.104a1.25 1.25 0 0 1 .476-2.066l4.166-1.44a.75.75 0 0 0 .426-.373l2.435-4.87a2.75 2.75 0 0 1 4.405-.715Zm3.766 5.886-4.826-4.826a1.25 1.25 0 0 0-2.002.325l-2.435 4.871a2.25 2.25 0 0 1-1.278 1.12l-3.789 1.31 6.705 6.704 1.308-3.789a2.25 2.25 0 0 1 1.12-1.277l4.872-2.436a1.25 1.25 0 0 0 .325-2.002Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 594 B

@@ -0,0 +1 @@
<svg width="24" height="24" fill="none" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12.012 2.25c.734.008 1.465.093 2.182.253a.75.75 0 0 1 .582.649l.17 1.527a1.384 1.384 0 0 0 1.927 1.116l1.401-.615a.75.75 0 0 1 .85.174 9.792 9.792 0 0 1 2.204 3.792.75.75 0 0 1-.271.825l-1.242.916a1.381 1.381 0 0 0 0 2.226l1.243.915a.75.75 0 0 1 .272.826 9.797 9.797 0 0 1-2.204 3.792.75.75 0 0 1-.848.175l-1.407-.617a1.38 1.38 0 0 0-1.926 1.114l-.169 1.526a.75.75 0 0 1-.572.647 9.518 9.518 0 0 1-4.406 0 .75.75 0 0 1-.572-.647l-.168-1.524a1.382 1.382 0 0 0-1.926-1.11l-1.406.616a.75.75 0 0 1-.849-.175 9.798 9.798 0 0 1-2.204-3.796.75.75 0 0 1 .272-.826l1.243-.916a1.38 1.38 0 0 0 0-2.226l-1.243-.914a.75.75 0 0 1-.271-.826 9.793 9.793 0 0 1 2.204-3.792.75.75 0 0 1 .85-.174l1.4.615a1.387 1.387 0 0 0 1.93-1.118l.17-1.526a.75.75 0 0 1 .583-.65c.717-.159 1.45-.243 2.201-.252Zm0 1.5a9.135 9.135 0 0 0-1.354.117l-.109.977A2.886 2.886 0 0 1 6.525 7.17l-.898-.394a8.293 8.293 0 0 0-1.348 2.317l.798.587a2.881 2.881 0 0 1 0 4.643l-.799.588c.32.842.776 1.626 1.348 2.322l.905-.397a2.882 2.882 0 0 1 4.017 2.318l.11.984c.889.15 1.798.15 2.687 0l.11-.984a2.881 2.881 0 0 1 4.018-2.322l.905.396a8.296 8.296 0 0 0 1.347-2.318l-.798-.588a2.881 2.881 0 0 1 0-4.643l.796-.587a8.293 8.293 0 0 0-1.348-2.317l-.896.393a2.884 2.884 0 0 1-4.023-2.324l-.11-.976a8.988 8.988 0 0 0-1.333-.117ZM12 8.25a3.75 3.75 0 1 1 0 7.5 3.75 3.75 0 0 1 0-7.5Zm0 1.5a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" fill="#212121"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,24 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6.2C4 4.98497 4.98497 4 6.2 4H28.2C29.415 4 30.4 4.98497 30.4 6.2V28.2C30.4 29.415 29.415 30.4 28.2 30.4H6.2C4.98497 30.4 4 29.415 4 28.2V6.2Z" fill="url(#paint0_radial_520_19)"/>
<path d="M32.6 6.2C32.6 4.98497 33.585 4 34.8 4H56.8C58.015 4 59 4.98497 59 6.2V28.2C59 29.415 58.015 30.4 56.8 30.4H34.8C33.585 30.4 32.6 29.415 32.6 28.2V6.2Z" fill="url(#paint1_radial_520_19)"/>
<path d="M32.6 34.8C32.6 33.585 33.585 32.6 34.8 32.6H56.8C58.015 32.6 59 33.585 59 34.8V56.8C59 58.015 58.015 59 56.8 59H34.8C33.585 59 32.6 58.015 32.6 56.8V34.8Z" fill="url(#paint2_radial_520_19)"/>
<path d="M4 34.8C4 33.585 4.98497 32.6 6.2 32.6H28.2C29.415 32.6 30.4 33.585 30.4 34.8V56.8C30.4 58.015 29.415 59 28.2 59H6.2C4.98497 59 4 58.015 4 56.8V34.8Z" fill="url(#paint3_radial_520_19)"/>
<defs>
<radialGradient id="paint0_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint1_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint2_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint3_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,24 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6.2C4 4.98497 4.98497 4 6.2 4H28.2C29.415 4 30.4 4.98497 30.4 6.2V28.2C30.4 29.415 29.415 30.4 28.2 30.4H6.2C4.98497 30.4 4 29.415 4 28.2V6.2Z" fill="url(#paint0_radial_519_6)"/>
<path d="M32.6 6.2C32.6 4.98497 33.585 4 34.8 4H56.8C58.015 4 59 4.98497 59 6.2V28.2C59 29.415 58.015 30.4 56.8 30.4H34.8C33.585 30.4 32.6 29.415 32.6 28.2V6.2Z" fill="url(#paint1_radial_519_6)"/>
<path d="M32.6 34.8C32.6 33.585 33.585 32.6 34.8 32.6H56.8C58.015 32.6 59 33.585 59 34.8V56.8C59 58.015 58.015 59 56.8 59H34.8C33.585 59 32.6 58.015 32.6 56.8V34.8Z" fill="url(#paint2_radial_519_6)"/>
<path d="M4 34.8C4 33.585 4.98497 32.6 6.2 32.6H28.2C29.415 32.6 30.4 33.585 30.4 34.8V56.8C30.4 58.015 29.415 59 28.2 59H6.2C4.98497 59 4 58.015 4 56.8V34.8Z" fill="url(#paint3_radial_519_6)"/>
<defs>
<radialGradient id="paint0_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint1_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint2_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint3_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

@@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
version="1.1"
viewBox="0 0 16.933 16.933"
id="svg2"
sodipodi:docname="system-search-checked-dark.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="4.65625"
inkscape:cx="32"
inkscape:cy="32"
inkscape:window-width="1197"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g2" />
<defs
id="defs2">
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#565757;stop-opacity:1;"
offset="0"
id="stop3" />
<stop
style="stop-color:#4c4f52;stop-opacity:1;"
offset="1"
id="stop4" />
</linearGradient>
<linearGradient
id="linearGradient919"
x1="4.3106"
x2="14.36"
y1="8.4665"
y2="8.4665"
gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#3c8cff"
offset="0"
id="stop1"
style="stop-color:#5fe277;stop-opacity:1;" />
<stop
stop-color="#55b4ff"
offset="1"
id="stop2"
style="stop-color:#0078d3;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient4"
x1="-7.2925777"
y1="11.283"
x2="5.189723"
y2="11.283"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(11.677839,-7.3471437)" />
</defs>
<g
transform="translate(16.148 .19097)"
id="g2">
<rect
transform="rotate(45)"
x="2.891"
y="10.492"
width="7.4901"
height="1.583"
ry=".79147"
fill="#1955b9"
stroke-width="1.1651"
id="rect2"
style="stroke-width:1.16520206;stroke-dasharray:none;fill:#0175d1;fill-opacity:1" />
<circle
transform="rotate(120)"
cx="10.62641"
cy="3.9358561"
r="5.3520403"
fill="none"
stroke="url(#linearGradient919)"
stroke-linecap="round"
stroke-width="1.77822"
id="circle2"
style="fill:url(#linearGradient4);stroke:url(#linearGradient919);stroke-width:1.77822958;stroke-dasharray:none" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="64"
height="64"
version="1.1"
viewBox="0 0 16.933 16.933"
id="svg2"
sodipodi:docname="system-search-checked-light.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview2"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.5849319"
inkscape:cx="26.95548"
inkscape:cy="26.423963"
inkscape:window-width="1257"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<defs
id="defs2">
<linearGradient
id="linearGradient919"
x1="4.3106"
x2="14.36"
y1="8.4665"
y2="8.4665"
gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#3c8cff"
offset="0"
id="stop1"
style="stop-color:#5fe277;stop-opacity:1;" />
<stop
stop-color="#55b4ff"
offset="1"
id="stop2"
style="stop-color:#0078d3;stop-opacity:1;" />
</linearGradient>
</defs>
<rect
transform="rotate(45,7.8434785,19.587845)"
x="3.4592254"
y="10.492"
width="6.921874"
height="1.5830002"
ry="0.71336967"
fill="#1955b9"
stroke-width="1.1651"
id="rect2-5"
style="fill:#0077d4;fill-opacity:1;stroke-width:1.1652;stroke-dasharray:none" />
<circle
transform="rotate(120,8.0188717,4.757011)"
cx="10.62641"
cy="3.9358561"
r="5.3520403"
fill="none"
stroke="url(#linearGradient919)"
stroke-linecap="round"
stroke-width="1.77822"
id="circle2"
style="fill:none;stroke:url(#linearGradient919);stroke-width:1.77822958;stroke-dasharray:none" />
<g
transform="translate(16.148 .19097)"
id="g2" />
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -7,10 +7,11 @@
version="1.1"
viewBox="0 0 16.933 16.933"
id="svg2"
sodipodi:docname="system-search.svg"
sodipodi:docname="system-search-dark.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
@@ -24,15 +25,27 @@
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="4.65625"
inkscape:cx="32"
inkscape:cy="32"
inkscape:cy="31.892617"
inkscape:window-width="1061"
inkscape:window-height="1020"
inkscape:window-height="835"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g2" />
<defs
id="defs2">
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#565757;stop-opacity:1;"
offset="0"
id="stop3" />
<stop
style="stop-color:#4c4f52;stop-opacity:1;"
offset="1"
id="stop4" />
</linearGradient>
<linearGradient
id="linearGradient919"
x1="4.3106"
@@ -52,6 +65,15 @@
id="stop2"
style="stop-color:#d7dcdf;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient4"
x1="-7.2925777"
y1="11.283"
x2="5.189723"
y2="11.283"
gradientUnits="userSpaceOnUse" />
</defs>
<g
transform="translate(16.148 .19097)"
@@ -77,6 +99,6 @@
stroke-linecap="round"
stroke-width="1.77822"
id="circle2"
style="fill:#4f4f4f;fill-opacity:1;stroke:url(#linearGradient919)" />
style="fill:url(#linearGradient4);fill-opacity:1;stroke:url(#linearGradient919)" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="63.999989"
height="61.62962"
viewBox="0 0 26.999999 26"
fill="none"
version="1.1"
id="svg3"
sodipodi:docname="task-view-dark-pressed.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.0349946"
inkscape:cx="32.228695"
inkscape:cy="33.305746"
inkscape:window-width="1331"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g3" />
<g
id="g3"
transform="translate(0.03879728,0.03736111)">
<g
id="g4"
transform="translate(-1.6875003,2.5312505)">
<rect
opacity="0.9"
x="9.4441719"
width="17.938911"
height="15.248073"
rx="0.89694548"
fill="#ffffff"
id="rect1"
y="0.8962484"
style="fill:url(#linearGradient4);fill-opacity:1;stroke:#e2e2e2;stroke-width:0.179719;stroke-dasharray:none;stroke-opacity:1"
transform="translate(-2.3652457,0.93944352)" />
<rect
y="3.8563478"
width="17.953642"
height="15.260596"
rx="0.89768213"
fill="url(#paint0_linear_505_17)"
id="rect2"
x="5.1897082"
style="mix-blend-mode:exclusion;fill:url(#paint0_linear_505_17-3);stroke-width:0.89775;stroke-dasharray:none" />
</g>
<g
id="g1"
transform="translate(-2.3652457,0.93944351)">
<g
style="mix-blend-mode:exclusion;stroke-width:1.00007574;stroke-dasharray:none"
id="g2"
transform="matrix(0.89768215,0,0,0.89768215,4.1799533,-0.09973431)" />
</g>
</g>
<defs
id="defs3">
<linearGradient
id="paint0_linear_505_17"
x1="-2.75374e-08"
y1="9.2399397"
x2="18.808399"
y2="26.838499"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#717171"
id="stop2"
offset="0"
style="stop-color:#717171;stop-opacity:0.73333335;" />
<stop
offset="1"
stop-color="#4F4F4F"
id="stop3"
style="stop-color:#4f4f4f;stop-opacity:0.73333335;" />
</linearGradient>
<linearGradient
id="paint0_linear_505_17-3"
x1="-2.75374e-08"
y1="9.2399397"
x2="18.808399"
y2="26.838499"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.89768213,0,0,0.89768213,5.189708,-4.2227915)">
<stop
stop-color="#717171"
id="stop2-6"
offset="0"
style="stop-color:#717171;stop-opacity:0.60000002;" />
<stop
offset="1"
stop-color="#4F4F4F"
id="stop3-7"
style="stop-color:#565656;stop-opacity:0.60000002;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient4"
x1="7.1907158"
y1="8.3216944"
x2="24.995911"
y2="10.507897"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(2.3203156,-0.89451377)" />
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop1" />
<stop
style="stop-color:#e6e6e6;stop-opacity:1;"
offset="1"
id="stop4" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -0,0 +1,108 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="63.999989"
height="61.62962"
viewBox="0 0 26.999999 26"
fill="none"
version="1.1"
id="svg3"
sodipodi:docname="task-view-checked-light.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview3"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="4.7640337"
inkscape:cx="21.200522"
inkscape:cy="37.573202"
inkscape:window-width="1238"
inkscape:window-height="612"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g3" />
<g
id="g3"
transform="translate(0.03879728,0.03736111)">
<g
id="g1"
transform="translate(-1.7324301,2.5761801)">
<g
style="mix-blend-mode:exclusion;stroke-width:0.999982;stroke-dasharray:none"
id="g2"
transform="matrix(0.89768215,0,0,0.89768215,5.2346381,-4.2677212)">
<rect
y="9"
width="20"
height="17"
rx="1"
fill="url(#paint0_linear_505_17)"
id="rect2"
x="0"
style="fill:url(#paint0_linear_505_17);stroke-width:0.999982;stroke-dasharray:none" />
</g>
<rect
opacity="0.9"
x="7.1238561"
width="17.938911"
height="15.248073"
rx="0.89694548"
fill="#ffffff"
id="rect1"
y="1.7907622"
style="fill:url(#linearGradient4);fill-opacity:1;stroke:#d4d4d4;stroke-width:0.179719;stroke-dasharray:none;stroke-opacity:1" />
</g>
</g>
<defs
id="defs3">
<linearGradient
id="linearGradient1"
inkscape:collect="always">
<stop
style="stop-color:#ffffff;stop-opacity:0.80000001;"
offset="0"
id="stop1" />
<stop
style="stop-color:#e6e6e6;stop-opacity:0.80000001;"
offset="1"
id="stop4" />
</linearGradient>
<linearGradient
id="paint0_linear_505_17"
x1="-2.75374e-08"
y1="9.2399397"
x2="18.808399"
y2="26.838499"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#717171"
id="stop2"
offset="0"
style="stop-color:#464543;stop-opacity:1;" />
<stop
offset="1"
stop-color="#4F4F4F"
id="stop3"
style="stop-color:#1f1f1f;stop-opacity:1;" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1"
id="linearGradient4"
x1="7.1729918"
y1="8.4772339"
x2="25.013634"
y2="10.352362"
gradientUnits="userSpaceOnUse" />
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

@@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import qs.modules.common.functions
Singleton {
id: root
@@ -147,6 +148,7 @@ Singleton {
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
property string taskManager: "plasma-systemmonitor --page-name Processes"
property string terminal: "kitty -1" // This is only for shell actions
property string update: "kitty -1 --hold=yes fish -i -c 'sudo pacman -Syu'"
property string volumeMixer: `~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol"`
}
@@ -158,7 +160,8 @@ Singleton {
property string placementStrategy: "leastBusy" // "free", "leastBusy", "mostBusy"
property real x: 100
property real y: 100
property string style: "cookie" // Options: "cookie", "digital"
property string style: "cookie" // Options: "cookie", "digital"
property string styleLocked: "cookie" // Options: "cookie", "digital"
property JsonObject cookie: JsonObject {
property bool aiStyling: false
property int sides: 14
@@ -236,13 +239,6 @@ Singleton {
property bool showPerformanceProfileToggle: false
property bool showScreenRecord: false
}
property JsonObject tray: JsonObject {
property bool monochromeIcons: true
property bool showItemId: false
property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area
property list<string> pinnedItems: [ ]
property bool filterPassive: true
}
property JsonObject workspaces: JsonObject {
property bool monochromeIcons: true
property int shown: 10
@@ -264,6 +260,9 @@ Singleton {
property bool showUnreadCount: false
}
}
property JsonObject tooltips: JsonObject {
property bool clickToShow: false
}
}
property JsonObject battery: JsonObject {
@@ -426,6 +425,14 @@ Singleton {
property int historyLength: 60
}
property JsonObject tray: JsonObject {
property bool monochromeIcons: true
property bool showItemId: false
property bool invertPinnedItems: true // Makes the below a whitelist for the tray and blacklist for the pinned area
property list<var> pinnedItems: [ "Fcitx" ]
property bool filterPassive: true
}
property JsonObject musicRecognition: JsonObject {
property int timeout: 16
property int interval: 4
@@ -532,6 +539,12 @@ Singleton {
}
property bool secondPrecision: false
}
property JsonObject updates: JsonObject {
property int checkInterval: 120 // minutes
property int adviseUpdateThreshold: 75 // packages
property int stronglyAdviseUpdateThreshold: 200 // packages
}
property JsonObject wallpaperSelector: JsonObject {
property bool useSystemFileDialog: false
@@ -559,6 +572,10 @@ Singleton {
}
property JsonObject waffles: JsonObject {
// Animations on Windoes are kinda janky. Set the following to
// false will make (some) stuff also be like that for accuracy.
// Example: the right-click menu of the Start button
property bool smootherAnimations: true
property JsonObject bar: JsonObject {
property bool bottom: true
property bool leftAlignApps: false
@@ -32,7 +32,9 @@ Singleton {
property string shellConfig: FileUtils.trimFileProtocol(`${Directories.config}/illogical-impulse`)
property string shellConfigName: "config.json"
property string shellConfigPath: `${Directories.shellConfig}/${Directories.shellConfigName}`
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
property string todoPath: FileUtils.trimFileProtocol(`${Directories.state}/user/todo.json`)
property string notesPath: FileUtils.trimFileProtocol(`${Directories.state}/user/notes.txt`)
property string conflictCachePath: FileUtils.trimFileProtocol(`${Directories.cache}/conflict-killer`)
property string notificationsPath: FileUtils.trimFileProtocol(`${Directories.cache}/notifications/notifications.json`)
property string generatedMaterialThemePath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/colors.json`)
property string generatedWallpaperCategoryPath: FileUtils.trimFileProtocol(`${Directories.state}/user/generated/wallpaper/category.txt`)
@@ -131,6 +131,14 @@ Singleton {
property real height: 600
property int tabIndex: 0
}
property JsonObject notes: JsonObject {
property bool pinned: false
property bool clickthrough: true
property real x: 1400
property real y: 42
property real width: 460
property real height: 330
}
}
property JsonObject timer: JsonObject {
@@ -285,4 +285,14 @@ Singleton {
}
return str;
}
function toTitleCase(str) {
// Replace "-" and "_" with space, then capitalize each word
return str.replace(/[-_]/g, " ").replace(
/\w\S*/g,
function(txt) {
return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
}
);
}
}
@@ -6,11 +6,13 @@ import qs.modules.common.widgets
Loader {
id: root
property bool shown: true
property alias fade: opacityBehavior.enabled
opacity: shown ? 1 : 0
visible: opacity > 0
active: opacity > 0
Behavior on opacity {
id: opacityBehavior
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
}
@@ -18,10 +18,9 @@ StyledListView { // Scrollable window
required property int index
required property var modelData
popup: root.popup
anchors.left: parent?.left
anchors.right: parent?.right
width: ListView.view.width // https://doc.qt.io/qt-6/qml-qtquick-listview.html
notificationGroup: popup ?
Notifications.popupGroupsByAppName[modelData] :
Notifications.groupsByAppName[modelData]
}
}
}
@@ -14,6 +14,10 @@ Item {
property real horizontalPadding: 10
property real verticalPadding: 5
function updateAnchor() {
tooltipLoader.item?.anchor.updateAnchor();
}
readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition
property var anchorEdges: Edges.Top
property var anchorGravity: anchorEdges
@@ -10,6 +10,7 @@ TextArea {
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
selectionColor: Appearance.colors.colSecondaryContainer
placeholderTextColor: Appearance.m3colors.m3outline
color: Appearance.colors.colOnLayer0
font {
family: Appearance.font.family.main
pixelSize: Appearance?.font.pixelSize.small ?? 15
@@ -5,7 +5,6 @@ import qs.services
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt.labs.synchronizer
Item {
id: root
@@ -16,7 +16,7 @@ AbstractBackgroundWidget {
implicitHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
readonly property string clockStyle: Config.options.background.widgets.clock.style
readonly property string clockStyle: GlobalStates.screenLocked ? Config.options.background.widgets.clock.styleLocked : Config.options.background.widgets.clock.style
readonly property bool forceCenter: (GlobalStates.screenLocked && Config.options.lock.centerClock)
readonly property bool shouldShow: (!Config.options.background.widgets.clock.showOnlyWhenLocked || GlobalStates.screenLocked)
property bool wallpaperSafetyTriggered: false
@@ -44,6 +44,7 @@ AbstractBackgroundWidget {
id: cookieClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "cookie" && (root.shouldShow)
fade: false
sourceComponent: Column {
spacing: 10
CookieClock {
@@ -61,6 +62,7 @@ AbstractBackgroundWidget {
id: digitalClockLoader
anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "digital" && (root.shouldShow)
fade: false
sourceComponent: ColumnLayout {
id: clockColumn
spacing: 6
@@ -16,7 +16,7 @@ MouseArea {
implicitWidth: batteryProgress.implicitWidth
implicitHeight: Appearance.sizes.barHeight
hoverEnabled: true
hoverEnabled: !Config.options.bar.tooltips.clickToShow
ClippedProgressBar {
id: batteryProgress
@@ -40,8 +40,7 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
hoverEnabled: !Config.options.bar.tooltips.clickToShow
ClockWidgetPopup {
hoverTarget: mouseArea
@@ -9,7 +9,7 @@ MouseArea {
property bool alwaysShowAllResources: false
implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin
implicitHeight: Appearance.sizes.barHeight
hoverEnabled: true
hoverEnabled: !Config.options.bar.tooltips.clickToShow
RowLayout {
id: rowLayout
@@ -1,10 +1,11 @@
import qs.modules.common
import qs.modules.common.widgets
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Quickshell.Services.SystemTray
import qs.services
import qs.modules.common
import qs.modules.common.widgets
Item {
id: root
@@ -17,13 +18,8 @@ Item {
property bool showOverflowMenu: true
property var activeMenu: null
property bool smartTray: Config.options.bar.tray.filterPassive
property list<var> itemsInUserList: SystemTray.items.values.filter(i => (Config.options.bar.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive)))
property list<var> itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.bar.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive)))
property bool invertPins: Config.options.bar.tray.invertPinnedItems
property list<var> pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList
property list<var> unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList
property list<var> pinnedItems: TrayService.pinnedItems
property list<var> unpinnedItems: TrayService.unpinnedItems
onUnpinnedItemsChanged: {
if (unpinnedItems.length == 0) root.closeOverflowMenu();
}
@@ -1,11 +1,12 @@
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Services.SystemTray
import Quickshell.Widgets
import Qt5Compat.GraphicalEffects
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.common.functions
MouseArea {
id: root
@@ -31,10 +32,7 @@ MouseArea {
event.accepted = true;
}
onEntered: {
tooltip.text = item.tooltipTitle.length > 0 ? item.tooltipTitle
: (item.title.length > 0 ? item.title : item.id);
if (item.tooltipDescription.length > 0) tooltip.text += " • " + item.tooltipDescription;
if (Config.options.bar.tray.showItemId) tooltip.text += "\n[" + item.id + "]";
tooltip.text = TrayService.getTooltipForItem(root.item);
}
Loader {
@@ -65,7 +63,7 @@ MouseArea {
IconImage {
id: trayIcon
visible: !Config.options.bar.tray.monochromeIcons
visible: !Config.options.tray.monochromeIcons
source: root.item.icon
anchors.centerIn: parent
width: parent.width
@@ -73,7 +71,7 @@ MouseArea {
}
Loader {
active: Config.options.bar.tray.monochromeIcons
active: Config.options.tray.monochromeIcons
anchors.fill: trayIcon
sourceComponent: Item {
Desaturate {
@@ -13,15 +13,19 @@ MouseArea {
implicitWidth: rowLayout.implicitWidth + 10 * 2
implicitHeight: Appearance.sizes.barHeight
hoverEnabled: true
acceptedButtons: Qt.LeftButton | Qt.RightButton
hoverEnabled: !Config.options.bar.tooltips.clickToShow
onPressed: {
Weather.getData();
Quickshell.execDetached(["notify-send",
Translation.tr("Weather"),
Translation.tr("Refreshing (manually triggered)")
, "-a", "Shell"
])
if (mouse.button === Qt.RightButton) {
Weather.getData();
Quickshell.execDetached(["notify-send",
Translation.tr("Weather"),
Translation.tr("Refreshing (manually triggered)")
, "-a", "Shell"
])
mouse.accepted = false
}
}
RowLayout {
@@ -5,7 +5,6 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Qt.labs.synchronizer
import Quickshell.Io
import Quickshell
import Quickshell.Wayland
@@ -67,11 +67,7 @@ DockButton {
}
altAction: () => {
if (Config.options.dock.pinnedApps.indexOf(appToplevel.appId) !== -1) {
Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.filter(id => id !== appToplevel.appId)
} else {
Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.concat([appToplevel.appId])
}
TaskbarApps.togglePin(appToplevel.appId);
}
contentItem: Loader {
@@ -25,7 +25,8 @@ Scope {
exclusionMode: ExclusionMode.Ignore
WlrLayershell.namespace: "quickshell:overlay"
WlrLayershell.layer: WlrLayer.Overlay
WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None
// Use OnDemand for pinned widgets to allow focus switching with mouse clicks
WlrLayershell.keyboardFocus: GlobalStates.overlayOpen ? WlrKeyboardFocus.Exclusive : (OverlayContext.clickableWidgets.length > 0 ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None)
visible: true
color: "transparent"
@@ -11,6 +11,7 @@ Singleton {
{ identifier: "floatingImage", materialSymbol: "imagesmode" },
{ identifier: "recorder", materialSymbol: "screen_record" },
{ identifier: "resources", materialSymbol: "browse_activity" },
{ identifier: "notes", materialSymbol: "note_stack" },
{ identifier: "volumeMixer", materialSymbol: "volume_up" },
]
@@ -8,10 +8,11 @@ import Quickshell
import Quickshell.Bluetooth
import qs.modules.ii.overlay.crosshair
import qs.modules.ii.overlay.volumeMixer
import qs.modules.ii.overlay.floatingImage
import qs.modules.ii.overlay.fpsLimiter
import qs.modules.ii.overlay.recorder
import qs.modules.ii.overlay.resources
import qs.modules.ii.overlay.floatingImage
import qs.modules.ii.overlay.notes
DelegateChooser {
id: root
@@ -22,5 +23,6 @@ DelegateChooser {
DelegateChoice { roleValue: "fpsLimiter"; FpsLimiter {} }
DelegateChoice { roleValue: "recorder"; Recorder {} }
DelegateChoice { roleValue: "resources"; Resources {} }
DelegateChoice { roleValue: "notes"; Notes {} }
DelegateChoice { roleValue: "volumeMixer"; VolumeMixer {} }
}
@@ -0,0 +1,17 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.ii.overlay
StyledOverlayWidget {
id: root
title: Translation.tr("Notes")
showCenterButton: true
contentItem: NotesContent {
radius: root.contentRadius
isClickthrough: root.clickthrough
}
}
@@ -0,0 +1,292 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.ii.overlay
OverlayBackground {
id: root
property alias content: textInput.text
property bool pendingReload: false
property var copyListEntries: []
property string lastParsedCopylistText: ""
property var parsedCopylistLines: []
property bool isClickthrough: false
property real maxCopyButtonSize: 20
Component.onCompleted: {
noteFile.reload();
updateCopyListEntries();
}
function saveContent() {
if (!textInput)
return;
noteFile.setText(root.content);
}
function focusAtEnd() {
if (!textInput)
return;
textInput.forceActiveFocus();
const endPos = root.content.length;
applySelection(endPos, endPos);
}
function applySelection(cursorPos, anchorPos) {
if (!textInput)
return;
const textLength = root.content.length;
const cursor = Math.max(0, Math.min(cursorPos, textLength));
const anchor = Math.max(0, Math.min(anchorPos, textLength));
textInput.select(anchor, cursor);
if (cursor === anchor)
textInput.deselect();
}
function scheduleCopylistUpdate(immediate = false) {
if (!textInput)
return;
if (immediate) {
copyListDebounce?.stop();
updateCopyListEntries();
} else {
copyListDebounce.restart();
}
}
function updateCopyListEntries() {
if (!textInput)
return;
const textValue = root.content;
if (!textValue || textValue.length === 0) {
lastParsedCopylistText = "";
parsedCopylistLines = [];
root.copyListEntries = [];
return;
}
if (textValue !== lastParsedCopylistText) {
const lineRegex = /(.*?)(\r?\n|$)/g;
let match = null;
const parsed = [];
while ((match = lineRegex.exec(textValue)) !== null) {
const lineText = match[1];
const newlineText = match[2];
const lineStart = match.index;
const lineEnd = lineStart + lineText.length;
const bulletMatch = lineText.match(/^\s*-\s+(.*\S)\s*$/);
if (bulletMatch) {
parsed.push({
content: bulletMatch[1].trim(),
start: lineStart,
end: lineEnd
});
}
if (newlineText === "")
break;
}
lastParsedCopylistText = textValue;
parsedCopylistLines = parsed;
if (parsed.length === 0) {
root.copyListEntries = [];
return;
}
}
updateCopylistPositions();
}
function updateCopylistPositions() {
if (!textInput || parsedCopylistLines.length === 0)
return;
const rawSelectionStart = textInput.selectionStart;
const rawSelectionEnd = textInput.selectionEnd;
const selectionStart = rawSelectionStart === -1 ? textInput.cursorPosition : rawSelectionStart;
const selectionEnd = rawSelectionEnd === -1 ? textInput.cursorPosition : rawSelectionEnd;
const rangeStart = Math.min(selectionStart, selectionEnd);
const rangeEnd = Math.max(selectionStart, selectionEnd);
const entries = parsedCopylistLines.map(line => {
// Don't show copy button if line is (partially) selected
const caretIntersects = rangeEnd > line.start && rangeStart <= line.end;
if (caretIntersects)
return null;
const startRect = textInput.positionToRectangle(line.start);
let endRect = textInput.positionToRectangle(line.end);
if (!isFinite(startRect.y))
return null;
if (!isFinite(endRect.y))
endRect = startRect;
const lineBottom = endRect.y + endRect.height;
const rectHeight = Math.max(lineBottom - startRect.y, textInput.font.pixelSize + 8);
return {
content: line.content,
y: startRect.y,
height: rectHeight
};
}).filter(entry => entry !== null);
root.copyListEntries = entries;
}
implicitWidth: 300
implicitHeight: 200
ColumnLayout {
id: contentItem
anchors.fill: parent
spacing: -16
ScrollView {
id: editorScrollView
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
ScrollBar.vertical.policy: ScrollBar.AsNeeded
onWidthChanged: root.scheduleCopylistUpdate(true)
StyledTextArea { // This has to be a direct child of ScrollView for proper scrolling
id: textInput
anchors {
left: parent.left
right: parent.right
}
wrapMode: TextEdit.Wrap
placeholderText: Translation.tr("Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep")
selectByMouse: true
persistentSelection: true
textFormat: TextEdit.PlainText
background: null
padding: 24
onTextChanged: {
if (textInput.activeFocus) {
saveDebounce.restart();
}
root.scheduleCopylistUpdate(true);
}
onHeightChanged: root.scheduleCopylistUpdate(true)
onContentHeightChanged: root.scheduleCopylistUpdate(true)
onCursorPositionChanged: root.scheduleCopylistUpdate()
onSelectionStartChanged: root.scheduleCopylistUpdate()
onSelectionEndChanged: root.scheduleCopylistUpdate()
}
Item {
anchors.fill: parent
visible: root.copyListEntries.length > 0
clip: true
Repeater {
model: ScriptModel {
values: root.copyListEntries
}
delegate: RippleButton {
id: copyButton
required property var modelData
readonly property real lineHeight: Math.min(Math.max(modelData.height, Appearance.font.pixelSize.normal + 6), root.maxCopyButtonSize)
readonly property real iconSizeLocal: Appearance.font.pixelSize.normal
readonly property real hitPadding: 6
property bool justCopied: false
implicitHeight: lineHeight
implicitWidth: lineHeight
buttonRadius: height / 2
y: modelData.y
anchors.right: parent.right
anchors.rightMargin: 10
z: 5
Timer {
id: resetState
interval: 700
onTriggered: {
copyButton.justCopied = false;
}
}
onClicked: {
Quickshell.clipboardText = copyButton.modelData.content;
justCopied = true;
resetState.start();
}
contentItem: Item {
anchors.centerIn: parent
MaterialSymbol {
id: iconItem
anchors.centerIn: parent
text: copyButton.justCopied ? "check" : "content_copy"
iconSize: copyButton.iconSizeLocal
color: Appearance.colors.colOnLayer1
}
}
}
}
}
}
StyledText {
id: statusLabel
Layout.fillWidth: true
Layout.margins: 16
horizontalAlignment: Text.AlignRight
text: saveDebounce.running ? Translation.tr("Saving...") : Translation.tr("Saved ")
color: Appearance.colors.colSubtext
}
}
Timer {
id: saveDebounce
interval: 500
repeat: false
onTriggered: saveContent()
}
Timer {
id: copyListDebounce
interval: 100
repeat: false
onTriggered: updateCopylistPositions()
}
FileView {
id: noteFile
path: Qt.resolvedUrl(Directories.notesPath)
onLoaded: {
root.content = noteFile.text();
if (root.content !== root.content) {
const previousCursor = textInput.cursorPosition;
const previousAnchor = textInput.selectionStart;
root.content = root.content;
applySelection(previousCursor, previousAnchor);
}
if (pendingReload) {
pendingReload = false;
Qt.callLater(root.focusAtEnd);
}
Qt.callLater(root.updateCopyListEntries);
}
onLoadFailed: error => {
if (error === FileViewError.FileNotFound) {
root.content = "";
noteFile.setText(root.content);
if (pendingReload) {
pendingReload = false;
Qt.callLater(root.focusAtEnd);
}
Qt.callLater(root.updateCopyListEntries);
} else {
console.log("[Overlay Notes] Error loading file: " + error);
}
}
}
}
@@ -5,7 +5,6 @@ import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import Qt5Compat.GraphicalEffects
import Qt.labs.synchronizer
import qs
import qs.services
import qs.modules.common
@@ -167,6 +167,9 @@ Scope {
function toggle() {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
function workspacesToggle() {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
function close() {
GlobalStates.overviewOpen = false;
}
@@ -189,6 +192,14 @@ Scope {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
}
GlobalShortcut {
name: "overviewWorkspacesToggle"
description: "Toggles overview on press"
onPressed: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
}
}
GlobalShortcut {
name: "overviewClose"
description: "Closes overview"
@@ -285,48 +285,36 @@ Rectangle {
}
}
}
Repeater {
model: ScriptModel {
values: Array.from({ length: root.messageBlocks.length }, (msg, i) => {
return ({
type: root.messageBlocks[i].type
})
});
values: root.messageBlocks
}
delegate: DelegateChooser {
id: messageDelegate
role: "type"
DelegateChoice { roleValue: "code"; MessageCodeBlock {
required property int index
property var thisBlock: root.messageBlocks[index]
editing: root.editing
renderMarkdown: root.renderMarkdown
enableMouseSelection: root.enableMouseSelection
segmentContent: thisBlock.content
segmentLang: thisBlock.lang
segmentContent: modelData.content
segmentLang: modelData.lang
messageData: root.messageData
} }
DelegateChoice { roleValue: "think"; MessageThinkBlock {
required property int index
property var thisBlock: root.messageBlocks[index]
editing: root.editing
renderMarkdown: root.renderMarkdown
enableMouseSelection: root.enableMouseSelection
segmentContent: thisBlock.content
segmentContent: modelData.content
messageData: root.messageData
done: root.messageData?.done ?? false
completed: thisBlock.completed ?? false
completed: modelData.completed ?? false
} }
DelegateChoice { roleValue: "text"; MessageTextBlock {
required property int index
property var thisBlock: root.messageBlocks[index]
editing: root.editing
renderMarkdown: root.renderMarkdown
enableMouseSelection: root.enableMouseSelection
segmentContent: thisBlock.content
segmentContent: modelData.content
messageData: root.messageData
done: root.messageData?.done ?? false
forceDisableChunkSplitting: root.messageData?.content.includes("```") ?? true
@@ -156,7 +156,6 @@ Item {
property bool editing: root.editing
property bool renderMarkdown: root.renderMarkdown
property bool enableMouseSelection: root.enableMouseSelection
property string segmentContent: root.segmentContent
property var messageData: root.messageData
property bool done: root.done
@@ -165,8 +164,9 @@ Item {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
segmentContent: root.segmentContent
}
}
}
}
}
}
@@ -12,6 +12,7 @@ AndroidQuickToggleButton {
name: Translation.tr("Bluetooth")
statusText: BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("No device")
available: BluetoothStatus.available
toggled: BluetoothStatus.enabled
buttonIcon: BluetoothStatus.connected ? "bluetooth_connected" : BluetoothStatus.enabled ? "bluetooth" : "bluetooth_disabled"
mainAction: () => {
@@ -27,4 +28,3 @@ AndroidQuickToggleButton {
)
}
}
@@ -9,6 +9,7 @@ AndroidQuickToggleButton {
name: Translation.tr("EasyEffects")
available: EasyEffects.available
toggled: EasyEffects.active
buttonIcon: "graphic_eq"
@@ -15,6 +15,7 @@ GroupButton {
required property string name
required property var mainAction
property string statusText: toggled ? Translation.tr("Active") : Translation.tr("Inactive")
property bool available: true
required property real baseCellWidth
required property real baseCellHeight
@@ -42,6 +43,7 @@ GroupButton {
signal openMenu()
enabled: available || editMode
padding: 6
horizontalPadding: padding
verticalPadding: padding
@@ -52,8 +54,8 @@ GroupButton {
colBackgroundToggledActive: (altAction && expandedSize) ? Appearance.colors.colLayer2Active : Appearance.colors.colPrimaryActive
buttonRadius: toggled ? Appearance.rounding.large : height / 2
buttonRadiusPressed: Appearance.rounding.normal
property color colText: (toggled && !(altAction && expandedSize)) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer2
property color colIcon: expandedSize ? (root.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText
property color colText: (toggled && !(altAction && expandedSize) && enabled) ? Appearance.colors.colOnPrimary : ColorUtils.transparentize(Appearance.colors.colOnLayer2, enabled ? 0 : 0.7)
property color colIcon: expandedSize ? ((root.toggled) ? Appearance.colors.colOnPrimary : Appearance.colors.colOnLayer3) : colText
onClicked: {
if (root.expandedSize && root.altAction) root.altAction();
@@ -15,7 +15,7 @@ MouseArea {
readonly property bool isLow: percentage <= Config.options.battery.low / 100
implicitHeight: batteryProgress.implicitHeight
hoverEnabled: true
hoverEnabled: !Config.options.bar.tooltips.clickToShow
ClippedProgressBar {
id: batteryProgress
@@ -9,7 +9,7 @@ MouseArea {
property bool alwaysShowAllResources: false
implicitHeight: columnLayout.implicitHeight
implicitWidth: columnLayout.implicitWidth
hoverEnabled: true
hoverEnabled: !Config.options.bar.tooltips.clickToShow
ColumnLayout {
id: columnLayout
@@ -33,8 +33,7 @@ Item {
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
hoverEnabled: !Config.options.bar.tooltips.clickToShow
Bar.ClockWidgetPopup {
hoverTarget: mouseArea
@@ -28,7 +28,7 @@ MouseArea {
}
acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton | Qt.LeftButton
hoverEnabled: true
hoverEnabled: !Config.options.bar.tooltips.clickToShow
onPressed: (event) => {
if (event.button === Qt.MiddleButton) {
activePlayer.togglePlaying();
@@ -55,6 +55,20 @@ ContentPage {
ContentSection {
icon: "clock_loader_40"
title: Translation.tr("Widget: Clock")
id: settingsClock
function stylePresent(styleName) {
if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) {
return true;
}
if (Config.options.background.widgets.clock.styleLocked === styleName) {
return true;
}
return false;
}
readonly property bool digitalPresent: stylePresent("digital")
readonly property bool cookiePresent: stylePresent("cookie")
ConfigRow {
Layout.fillWidth: true
@@ -107,6 +121,7 @@ ContentPage {
}
ContentSubsection {
visible: !Config.options.background.widgets.clock.showOnlyWhenLocked
title: Translation.tr("Clock style")
ConfigSelectionArray {
currentValue: Config.options.background.widgets.clock.style
@@ -129,7 +144,29 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "digital"
title: Translation.tr("Clock style (locked)")
ConfigSelectionArray {
currentValue: Config.options.background.widgets.clock.styleLocked
onSelected: newValue => {
Config.options.background.widgets.clock.styleLocked = newValue;
}
options: [
{
displayName: Translation.tr("Digital"),
icon: "timer_10",
value: "digital"
},
{
displayName: Translation.tr("Cookie"),
icon: "cookie",
value: "cookie"
}
]
}
}
ContentSubsection {
visible: settingsClock.digitalPresent
title: Translation.tr("Digital clock settings")
ConfigSwitch {
@@ -143,7 +180,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Cookie clock settings")
ConfigSwitch {
@@ -197,7 +234,7 @@ ContentPage {
ConfigRow {
ConfigSwitch {
enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full"
enabled: Config.options.background.widgets.clock.cookie.dialNumberStyle === "dots" || Config.options.background.widgets.clock.cookie.dialNumberStyle === "full"
buttonIcon: "brightness_7"
text: Translation.tr("Hour marks")
checked: Config.options.background.widgets.clock.cookie.hourMarks
@@ -213,7 +250,7 @@ ContentPage {
}
ConfigSwitch {
enabled: Config.options.background.widgets.clock.style === "cookie" && Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers"
enabled: Config.options.background.widgets.clock.cookie.dialNumberStyle !== "numbers"
buttonIcon: "timer_10"
text: Translation.tr("Digits in the middle")
checked: Config.options.background.widgets.clock.cookie.timeIndicators
@@ -231,7 +268,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Dial style")
ConfigSelectionArray {
currentValue: Config.options.background.widgets.clock.cookie.dialNumberStyle
@@ -270,7 +307,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Hour hand")
ConfigSelectionArray {
currentValue: Config.options.background.widgets.clock.cookie.hourHandStyle
@@ -303,7 +340,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Minute hand")
ConfigSelectionArray {
@@ -342,7 +379,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Second hand")
ConfigSelectionArray {
@@ -376,7 +413,7 @@ ContentPage {
}
ContentSubsection {
visible: Config.options.background.widgets.clock.style === "cookie"
visible: settingsClock.cookiePresent
title: Translation.tr("Date style")
ConfigSelectionArray {
@@ -148,18 +148,18 @@ ContentPage {
ConfigSwitch {
buttonIcon: "keep"
text: Translation.tr('Make icons pinned by default')
checked: Config.options.bar.tray.invertPinnedItems
checked: Config.options.tray.invertPinnedItems
onCheckedChanged: {
Config.options.bar.tray.invertPinnedItems = checked;
Config.options.tray.invertPinnedItems = checked;
}
}
ConfigSwitch {
buttonIcon: "colors"
text: Translation.tr('Tint icons')
checked: Config.options.bar.tray.monochromeIcons
checked: Config.options.tray.monochromeIcons
onCheckedChanged: {
Config.options.bar.tray.monochromeIcons = checked;
Config.options.tray.monochromeIcons = checked;
}
}
}
@@ -334,4 +334,17 @@ ContentPage {
}
}
}
ContentSection {
icon: "tooltip"
title: Translation.tr("Tooltips")
ConfigSwitch {
buttonIcon: "ads_click"
text: Translation.tr("Click to show")
checked: Config.options.bar.tooltips.clickToShow
onCheckedChanged: {
Config.options.bar.tooltips.clickToShow = checked;
}
}
}
}
@@ -22,7 +22,7 @@ ContentPage {
// Use a nerdfont to see the icons
options: ([
"󰖳", "", "󰨡", "", "󰌽", "󰣇", "", "", "",
"", "", "󱄛", "", "", "⌘", "󰀲", "󰟍", ""
"", "", "󱄛", "", "", "", "⌘", "󰀲", "󰟍", ""
]).map(icon => { return {
displayName: icon,
value: icon
@@ -0,0 +1,37 @@
## Waffle
A recreation of Windoes. It's WIP!
- If you install illogical-impulse fully, you can press Super+Alt+W to switch to this style.
- If you're just copying the Quickshell config, run the config as usual (`qs -c ii`) then run `qs -c ii ipc call panelFamily cycle`
## From EWW version to Quickshell
Just a reflection, in case anyone's interested. My blog is probably a better place for this, but it does not exist. Besides, this is going to change as I do more stuff. Currently there's just the bar.
### Improvements
- QtQuick's `Button` has the `{top/bottom/left/right}Inset` properties, so we can have clickable regions expanding beyond the button background for free. With EWW it was annoying to wrap the button content with an `eventbox` that has some padding, then somehow use CSS selectors to make sure hovering effects work. I have to admit, (a large) part of that annoyance was with how bad my copy-pasting coding practice was at the time, but still...
- Fancy effects: Gtk3 CSS does not support transformations. In QtQuick we can smack `rotation` and `scale` almost everywhere, so it's simple to make bouncy icons and rotating chevrons
- Quickshell provides a system tray service (EWW does now but didn't at the time I created the EWW Windoes version), so now there's no Waybar needed for the tray.
- QtQuick has `Loader`s, so we can have this live-switchable from the main style without killing the widget system, moving styles to the correct folder, and relaunching.
- This time my computer is powerful enough to run a VM, so I don't have to occasionally reboot to take quick screenshots for reference. I try to make everything pixel-perfect so this is necessary. Speaking about pixel-perfectness, in the EWW version I hardcoded sizes, but this time I'm still doing that (lol), BUT that's normal and not a problem because Qt has the `QT_SCALE_FACTOR` env var for scaling. (Please feel free to prove me wrong in saying Gtk3 doesn't have that magic)
### Challenges
- Qt is not Gtk and definitely not React
- We don't get directional border on QtQuick `Rectangle`s like in CSS. I was able to get around this with manual drawing, but it was a bit more work
- Fluent Icons is difficult to use, compared to Material Symbols
- No React, so no clean use via a library.
- If we use the font, there's no proper, searchable **codepoint** cheatsheet like Nerd Fonts, and there's no ligatures
- I resorted to downloading individual SVGs. Not that nice, but it's better than scanning the whole table of icons every time I want one. For this we have fluenticon.com and fluenticons.co, but icons are awkwardly named and there's no alias. Why is the reload/refresh icon called "arrow-sync"? Well, the name is not misleading, but arguably reload/refresh are more common actions. From Fluent Design's [page on Iconography](https://fluent2.microsoft.design/iconography):
> Fluent system icons are literal metaphors and are named for the shape or object they represent, not the functionality they provide
"sync" is functionality.
@@ -0,0 +1,77 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WBarAttachedPanelContent {
id: root
contentItem: ColumnLayout {
anchors.centerIn: parent
spacing: 0
Rectangle {
Layout.fillHeight: true
Layout.fillWidth: true
topLeftRadius: root.border.radius - root.border.border.width
topRightRadius: topLeftRadius
color: Looks.colors.bgPanelBody
implicitWidth: 360
implicitHeight: 380
}
Rectangle {
Layout.fillHeight: false
Layout.fillWidth: true
color: Looks.colors.bgPanelSeparator
implicitHeight: 1
}
Rectangle {
Layout.fillHeight: false
Layout.fillWidth: true
bottomLeftRadius: root.border.radius - root.border.border.width
bottomRightRadius: bottomLeftRadius
color: Looks.colors.bgPanelFooter
implicitWidth: 360
implicitHeight: 47
// Battery button
WPanelFooterButton {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 12
contentItem: Row {
spacing: 4
FluentIcon {
anchors.verticalCenter: parent.verticalCenter
icon: WIcons.batteryIcon
}
WText {
anchors.verticalCenter: parent.verticalCenter
text: `${Math.round(Battery.percentage * 100) || 0}%`
}
}
}
// Settings button
WPanelFooterButton {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 12
contentItem: FluentIcon {
icon: "settings"
}
}
}
}
}
@@ -0,0 +1,85 @@
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
Scope {
id: root
Connections {
target: GlobalStates
function onSidebarLeftOpenChanged() {
if (GlobalStates.sidebarLeftOpen) barLoader.active = true;
}
}
Loader {
id: barLoader
active: GlobalStates.sidebarLeftOpen
sourceComponent: PanelWindow {
id: panelWindow
exclusiveZone: 0
WlrLayershell.namespace: "quickshell:actionCenter"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
bottom: Config.options.waffles.bar.bottom
top: !Config.options.waffles.bar.bottom
right: true
}
implicitWidth: content.implicitWidth + content.visualMargin * 2
implicitHeight: content.implicitHeight + content.visualMargin * 2
HyprlandFocusGrab {
id: focusGrab
active: true
windows: [panelWindow]
onCleared: content.close();
}
Connections {
target: GlobalStates
function onSidebarLeftOpenChanged() {
if (!GlobalStates.sidebarLeftOpen) content.close();
}
}
ActionCenterContent {
id: content
anchors.centerIn: parent
onClosed: {
barLoader.active = false;
GlobalStates.sidebarLeftOpen = false;
}
}
}
}
function toggleOpen() {
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
}
IpcHandler {
target: "sidebarLeft"
function toggle() {
root.toggleOpen();
}
}
GlobalShortcut {
name: "sidebarLeftToggle"
description: "Toggles left sidebar on press"
onPressed: root.toggleOpen();
}
}
@@ -17,10 +17,12 @@ BarButton {
rightInset: 2
implicitWidth: height - topInset - bottomInset + leftInset + rightInset
property real pressedScale: 5/6
onDownChanged: {
scaleAnim.duration = root.down ? 150 : 200
scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut
contentItem.scale = root.down ? 5/6 : 1 // If/When we do dragging, the scale is 1.25
contentItem.scale = root.down ? root.pressedScale : 1 // If/When we do dragging, the scale is 1.25
}
background: Item {
@@ -45,9 +47,7 @@ BarButton {
anchors.fill: parent
anchors.rightMargin: 5
active: root.multiple
sourceComponent: BackgroundAcrylicRectangle {
}
sourceComponent: BackgroundAcrylicRectangle {}
}
}
@@ -75,7 +75,7 @@ BarButton {
component BackgroundAcrylicRectangle: AcrylicRectangle {
shiny: ((root.hovered && !root.down) || root.checked)
color: root.colBackground
color: root.color
border.width: 1
border.color: root.colBackgroundBorder
@@ -13,6 +13,8 @@ Kirigami.Icon {
property real implicitSize: 26
implicitWidth: implicitSize
implicitHeight: implicitSize
animated: true
roundToIconSize: false
fallback: root.iconName
source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback
@@ -8,11 +8,14 @@ import qs.modules.waffle.looks
Button {
id: root
signal altAction()
signal middleClickAction()
property var altAction: () => {}
property var middleClickAction: () => {}
property color colBackground
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
property color colBackgroundHover: Looks.colors.bg1Hover
property color colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
Layout.fillHeight: true
topInset: 4
bottomInset: 4
@@ -37,16 +40,16 @@ Button {
}
}
colBackground: {
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.contentTransparency : 1)
color: {
if (root.down) {
return Looks.colors.bg1Active
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return Looks.colors.bg1Hover
return root.colBackgroundHover
} else {
return ColorUtils.transparentize(Looks.colors.bg1)
return root.colBackground
}
}
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, root.checked ? Looks.contentTransparency : 1)
MouseArea {
anchors.fill: parent
@@ -66,7 +69,8 @@ Button {
background: AcrylicRectangle {
shiny: ((root.hovered && !root.down) || root.checked)
color: root.colBackground
color: root.color
radius: Looks.radius.medium
border.width: 1
border.color: root.colBackgroundBorder
@@ -0,0 +1,41 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
import qs.modules.waffle.bar
BarButton {
id: root
property alias iconName: iconContent.icon
property alias iconSource: iconContent.source
property alias iconSize: iconContent.implicitSize
property alias iconRotation: iconContent.rotation
property alias iconMonochrome: iconContent.monochrome
property alias iconScale: iconContent.scale
property alias tooltipText: tooltip.text
property alias overlayingItems: iconContent.data
implicitWidth: 32
contentItem: Item {
anchors.centerIn: parent
implicitWidth: iconContent.implicitWidth
implicitHeight: iconContent.implicitHeight
FluentIcon {
id: iconContent
anchors.centerIn: parent
implicitSize: 16
icon: root.iconName
monochrome: false
}
}
BarToolTip {
id: tooltip
extraVisibleCondition: root.shouldShowTooltip && text !== ""
}
}
@@ -0,0 +1,59 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
BarPopup {
id: root
default property var menuData
property var model: [
{ iconName: "start-here", text: "Start", action: () => {print("hello")} },
{ type : "separator" },
]
readonly property bool hasIcons: model.some(item => item.iconName !== undefined && item.iconName !== "")
padding: 2
contentItem: ColumnLayout {
anchors.centerIn: parent
spacing: 0
Repeater {
model: root.model
delegate: DelegateChooser {
role: "type"
DelegateChoice {
roleValue: "separator"
Rectangle {
Layout.topMargin: 2
Layout.bottomMargin: 2
Layout.fillWidth: true
implicitHeight: 1
color: Looks.colors.bg0Border
}
}
DelegateChoice {
roleValue: undefined
WButton {
id: btn
Layout.fillWidth: true
required property var modelData
forceShowIcon: root.hasIcons
icon.name: modelData.iconName ? modelData.iconName : ""
monochromeIcon: modelData.monochromeIcon ?? true
text: modelData.text ? modelData.text : ""
onClicked: {
if (modelData.action) modelData.action();
root.close();
}
}
}
}
}
}
}
@@ -0,0 +1,131 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Loader {
id: root
required property var contentItem
property real padding: Looks.radius.large - Looks.radius.medium
property bool noSmoothClosing: !Config.options.waffles.smootherAnimations
property bool closeOnFocusLost: true
signal focusCleared()
property Item anchorItem: parent
property real visualMargin: 12
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
property real ambientShadowWidth: 1
onFocusCleared: {
if (!root.closeOnFocusLost) return;
root.close()
}
function grabFocus() { // Doesn't work
item.grabFocus();
}
function close() {
item.close();
}
function updateAnchor() {
item?.anchor.updateAnchor();
}
active: false
visible: active
sourceComponent: PopupWindow {
id: popupWindow
visible: true
Component.onCompleted: {
openAnim.start();
}
anchor {
adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX
item: root.anchorItem
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
}
HyprlandFocusGrab {
id: focusGrab
active: true
windows: [popupWindow]
onCleared: root.focusCleared();
}
function close() {
if (root.noSmoothClosing) root.active = false;
else closeAnim.start();
}
function grabFocus() {
focusGrab.active = true; // Doesn't work
}
implicitWidth: realContent.implicitWidth + (root.ambientShadowWidth * 2) + (root.visualMargin * 2)
implicitHeight: realContent.implicitHeight + (root.ambientShadowWidth * 2) + (root.visualMargin * 2)
property real sourceEdgeMargin: -implicitHeight
PropertyAnimation {
id: openAnim
target: popupWindow
property: "sourceEdgeMargin"
to: (root.ambientShadowWidth + root.visualMargin)
duration: 200
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
}
SequentialAnimation {
id: closeAnim
PropertyAnimation {
target: popupWindow
property: "sourceEdgeMargin"
to: -implicitHeight
duration: 150
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
}
ScriptAction {
script: {
root.active = false;
}
}
}
color: "transparent"
WAmbientShadow {
target: realContent
}
Rectangle {
id: realContent
z: 1
anchors {
left: parent.left
right: parent.right
top: root.barAtBottom ? undefined : parent.top
bottom: root.barAtBottom ? parent.bottom : undefined
margins: root.ambientShadowWidth + root.visualMargin
// Opening anim
bottomMargin: root.barAtBottom ? popupWindow.sourceEdgeMargin : (root.ambientShadowWidth + root.visualMargin)
topMargin: root.barAtBottom ? (root.ambientShadowWidth + root.visualMargin) : popupWindow.sourceEdgeMargin
}
color: Looks.colors.bg1
radius: Looks.radius.large
// test
implicitWidth: root.contentItem.implicitWidth + (root.padding * 2)
implicitHeight: root.contentItem.implicitHeight + (root.padding * 2)
children: [root.contentItem]
}
}
}
@@ -9,9 +9,10 @@ import qs.modules.waffle.looks
AppButton {
id: root
iconName: "system-search"
iconName: checked ? "system-search-checked" : "system-search"
separateLightDark: true
checked: GlobalStates.overviewOpen
onClicked: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now...
}
@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import Quickshell
import qs
import qs.services
import qs.modules.common
@@ -10,7 +11,7 @@ AppButton {
id: root
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
iconName: "start-here"
iconName: down ? "start-here-pressed" : "start-here"
onClicked: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now...
@@ -21,4 +22,45 @@ AppButton {
text: Translation.tr("Start")
extraVisibleCondition: root.shouldShowTooltip
}
altAction: () => {
contextMenu.active = true;
}
BarMenu {
id: contextMenu
model: [
{
text: Translation.tr("Terminal"),
action: () => {
Quickshell.execDetached(["bash", "-c", Config.options.apps.terminal]);
}
},
{
text: Translation.tr("Task Manager"),
action: () => {
Quickshell.execDetached(["bash", "-c", Config.options.apps.taskManager]);
}
},
{
text: Translation.tr("Settings"),
action: () => {
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath("settings.qml")]);
}
},
{
text: Translation.tr("File Explorer"),
action: () => {
Qt.openUrlExternally(Directories.home);
}
},
{
text: Translation.tr("Search"),
action: () => {
Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "overview", "toggle"]);
}
},
]
}
}
@@ -8,9 +8,9 @@ import qs.modules.waffle.looks
BarButton {
id: root
checked: GlobalStates.sidebarRightOpen
checked: GlobalStates.sidebarLeftOpen
onClicked: {
GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; // For now...
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen;
}
contentItem: Item {
@@ -85,11 +85,13 @@ BarButton {
BarToolTip {
extraVisibleCondition: root.shouldShowTooltip && volumeHoverArea.containsMouse
text: Translation.tr("Speakers (%1): %2") //
.arg(Audio.sink?.nickname || Audio.sink?.description || Translation.tr("Unknown")) //
.arg(`${Math.round(Audio.sink?.audio.volume * 100) || 0}%`) //
.arg(Audio.sink?.nickname || Audio.sink?.description || Translation.tr("Unknown")) //
.arg(Audio.sink?.audio.muted ? Translation.tr("Muted") : `${Math.round(Audio.sink?.audio.volume * 100) || 0}%`) //
}
BarToolTip {
extraVisibleCondition: root.shouldShowTooltip && batteryHoverArea.containsMouse
text: Translation.tr("Battery: %1").arg(`${Math.round(Battery.percentage * 100) || 0}%`)
text: Translation.tr("Battery: %1%2") //
.arg(`${Math.round(Battery.percentage * 100) || 0}%`) //
.arg(Battery.isPluggedIn ? (" " + Translation.tr("(Plugged in)")) : "")
}
}
@@ -9,7 +9,8 @@ import qs.modules.waffle.looks
AppButton {
id: root
iconName: "task-view"
iconName: (down && !checked) ? "task-view-pressed" : "task-view"
pressedScale: checked ? 5/6 : 1
separateLightDark: true
checked: GlobalStates.overviewOpen
@@ -0,0 +1,33 @@
import QtQuick
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
import qs.modules.waffle.bar.tray
BarIconButton {
id: root
visible: Updates.updateAdvised || Updates.updateStronglyAdvised
padding: 4
iconName: "arrow-sync"
iconSize: 20 // Needed because the icon appears to have some padding
tooltipText: Translation.tr("Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages").arg(Updates.count)
onClicked: {
Quickshell.execDetached(["bash", "-c", Config.options.apps.update]);
}
overlayingItems: Rectangle {
anchors {
right: parent.right
bottom: parent.bottom
margins: 1
}
implicitWidth: 8
implicitHeight: implicitWidth
radius: height / 2
color: Updates.updateStronglyAdvised ? Looks.colors.warning : Looks.colors.accent
}
}
@@ -9,9 +9,8 @@ import qs.modules.common
import qs.modules.common.widgets
Scope {
id: bar
property bool showBarBackground: Config.options.bar.showBackground
id: root
LazyLoader {
id: barLoader
active: GlobalStates.barOpen && !GlobalStates.screenLocked
@@ -4,6 +4,7 @@ import qs.modules.common
import qs.modules.common.widgets
import qs.modules.waffle.looks
import qs.modules.waffle.bar.tasks
import qs.modules.waffle.bar.tray
Rectangle {
id: root
@@ -68,6 +69,8 @@ Rectangle {
shown: Config.options.waffles.bar.leftAlignApps
sourceComponent: WidgetsButton {}
}
Tray {}
UpdatesButton {}
SystemButton {}
TimeButton {}
}
@@ -18,6 +18,11 @@ AppButton {
onClicked: {
GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen
}
onDownChanged: {
scaleAnim.duration = root.down ? 150 : 200
scaleAnim.easing.bezierCurve = root.down ? Looks.transition.easing.bezierCurve.easeIn : Looks.transition.easing.bezierCurve.easeOut
iconWidget.scale = root.down ? 5/6 : 1 // If/When we do dragging, the scale is 1.25
}
contentItem: Item {
anchors {
@@ -41,6 +46,13 @@ AppButton {
id: iconWidget
anchors.verticalCenter: parent.verticalCenter
iconName: root.iconName
Behavior on scale {
NumberAnimation {
id: scaleAnim
easing.type: Easing.BezierSpline
}
}
}
Column {
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Layouts
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
import qs.modules.waffle.bar
import Quickshell
@@ -16,6 +17,7 @@ AppButton {
property bool hasWindows: appEntry.toplevels.length > 0
signal hoverPreviewRequested()
signal hoverPreviewDismissed()
multiple: appEntry.toplevels.length > 1
checked: active
@@ -37,12 +39,18 @@ AppButton {
}
}
onMiddleClickAction: {
middleClickAction: () => {
if (root.desktopEntry) {
desktopEntry.execute()
}
}
altAction: () => {
root.hoverPreviewDismissed()
root.hoverTimer.stop()
contextMenu.active = true;
}
// Active indicator
Rectangle {
id: activeIndicator
@@ -74,4 +82,44 @@ AppButton {
extraVisibleCondition: root.shouldShowTooltip && !root.hasWindows
text: desktopEntry ? desktopEntry.name : appEntry.appId
}
BarMenu {
id: contextMenu
model: [
...((root.desktopEntry?.actions.length > 0) ? root.desktopEntry.actions.map(action =>({
iconName: action.icon,
text: action.name,
action: () => {
action.execute()
}
})).concat({ type: "separator" }) : []),
{
iconName: root.iconName,
text: root.desktopEntry ? root.desktopEntry.name : StringUtils.toTitleCase(appEntry.appId),
monochromeIcon: false,
action: () => {
if (root.desktopEntry) {
root.desktopEntry.execute()
}
}
},
{
iconName: root.appEntry.pinned ? "pin-off" : "pin",
text: root.appEntry.pinned ? qsTr("Unpin from taskbar") : qsTr("Pin to taskbar"),
action: () => {
TaskbarApps.togglePin(root.appEntry.appId);
}
},
...(root.appEntry.toplevels.length > 0 ? [{
iconName: "dismiss",
text: root.multiple ? qsTr("Close all windows") : qsTr("Close window"),
action: () => {
for (let toplevel of root.appEntry.toplevels) {
toplevel.close();
}
}
}] : []),
]
}
}
@@ -16,7 +16,7 @@ PopupWindow {
property Item anchorItem
//////////////////// Functions ////////////////////
function close() {
function close() { // Closing doesn't animate, not sure if they're just lazy or it's intentional
marginBehavior.enabled = false;
root.visible = false;
}
@@ -36,7 +36,7 @@ PopupWindow {
///////////////////// Internals /////////////////////
readonly property bool bottom: Config.options.waffles.bar.bottom
property real visualMargin: 12
property alias ambientShadowWidth: ambientShadow.border.width
property real ambientShadowWidth: 1
visible: false
color: "transparent"
@@ -64,16 +64,8 @@ PopupWindow {
hoverEnabled: true
// Shadow
Rectangle {
id: ambientShadow
anchors {
fill: contentItem
margins: -border.width
}
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency)
border.width: 1
color: "transparent"
radius: Looks.radius.large + border.width
WAmbientShadow {
target: contentItem
}
Rectangle {
@@ -17,10 +17,17 @@ MouseArea {
previewPopup.show(appEntry, button);
}
Behavior on implicitWidth {
animation: Looks.transition.move.createObject(this)
}
// Apps row
RowLayout {
id: row
anchors.fill: parent
anchors {
top: parent.top
bottom: parent.bottom
}
spacing: 0
Repeater {
@@ -36,6 +43,9 @@ MouseArea {
onHoverPreviewRequested: {
root.showPreviewPopup(appEntry, this)
}
onHoverPreviewDismissed: {
previewPopup.close()
}
}
}
}
@@ -46,4 +56,5 @@ MouseArea {
tasksHovered: root.containsMouse
anchor.window: root.QsWindow.window
}
}
@@ -0,0 +1,118 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Qt.labs.synchronizer
import Quickshell
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.waffle.looks
import qs.modules.waffle.bar
RowLayout {
id: root
property bool overflowOpen: false
property bool dragging: false
Layout.fillHeight: true
spacing: 0
BarIconButton {
id: overflowButton
visible: (TrayService.unpinnedItems.length > 0 || root.dragging)
checked: root.overflowOpen
iconName: "chevron-down"
iconMonochrome: true
iconRotation: (Config.options.waffles.bar.bottom ? 180 : 0) + (root.overflowOpen ? 180 : 0)
Behavior on iconRotation {
animation: Looks.transition.rotate.createObject(this)
}
onClicked: {
root.overflowOpen = !root.overflowOpen;
}
TrayOverflowMenu {
id: trayOverflowLayout
Synchronizer on active {
property alias source: root.overflowOpen
}
}
BarToolTip {
extraVisibleCondition: overflowButton.shouldShowTooltip
text: qsTr("Show hidden icons")
}
DropArea {
id: pinDropArea
anchors.fill: parent
property bool willPin: false
onEntered: willPin = true
onExited: willPin = false
}
}
Repeater {
model: ScriptModel {
values: TrayService.pinnedItems
}
delegate: TrayButton {
id: trayButton
required property var modelData
item: modelData
property real initialX
property real initialY
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
drag.axis: Drag.XAxis
drag.threshold: 2
onPressed: event => {
trayButton.Drag.hotSpot.x = event.x;
trayButton.initialX = trayButton.x;
root.dragging = true;
trayButton.Drag.active = true;
}
onPositionChanged: {
pinTooltip.updateAnchor();
}
onReleased: {
if (!dragArea.drag.active) {
trayButton.click();
} else {
if (pinDropArea.containsDrag && pinDropArea.willPin) {
// Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out...
trayButton.visible = false;
TrayService.togglePin(trayButton.item.id);
pinDropArea.willPin = false;
} else {
trayButton.x = trayButton.initialX;
}
}
trayButton.Drag.active = false;
root.dragging = false;
}
}
BarToolTip {
id: pinTooltip
extraVisibleCondition: trayButton.Drag.active && pinDropArea.containsDrag && pinDropArea.willPin
realContentHorizontalPadding: 6
realContentVerticalPadding: 6
realContentItem: FluentIcon {
anchors.centerIn: parent
icon: "pin-off"
implicitSize: 18
}
}
}
}
}
@@ -0,0 +1,52 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
import Quickshell.Services.SystemTray
import qs.services
import qs.modules.common
import qs.modules.common.widgets
import qs.modules.waffle.looks
import qs.modules.waffle.bar
BarIconButton {
id: root
required property SystemTrayItem item
property alias menuOpen: menu.visible
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
iconSource: item.icon
iconScale: 0
Component.onCompleted: {
root.iconScale = 1
}
Behavior on iconScale {
animation: Looks.transition.enter.createObject(this)
}
onClicked: {
item.activate();
}
altAction: () => {
if (item.hasMenu) menu.open()
}
// This is lazy, but it's not like tray menus on Windoes are consistent...
// TODO: Figure out how to do cascading menus then use a custom menu
QsMenuAnchor {
id: menu
menu: root.item.menu
anchor {
adjustment: PopupAdjustment.ResizeY | PopupAdjustment.SlideX
item: root
gravity: root.barAtBottom ? Edges.Top : Edges.Bottom
edges: root.barAtBottom ? Edges.Top : Edges.Bottom
}
}
BarToolTip {
extraVisibleCondition: root.shouldShowTooltip && !root.Drag.active
text: TrayService.getTooltipForItem(root.item)
}
}
@@ -0,0 +1,124 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
import qs.modules.waffle.bar
BarPopup {
id: root
closeOnFocusLost: false
onFocusCleared: {
const hasMenuOpen = contentItem.children.some(c => (c.menuOpen));
if (!hasMenuOpen)
root.close();
else
root.grabFocus();
}
contentItem: Item {
id: contentItem
anchors.centerIn: parent
implicitWidth: contentGrid.implicitWidth
implicitHeight: contentGrid.implicitHeight
GridLayout {
id: contentGrid
anchors.centerIn: parent
rows: Math.floor(Math.sqrt(TrayService.unpinnedItems.length))
columns: Math.ceil(TrayService.unpinnedItems.length / rows)
columnSpacing: 0
rowSpacing: 0
Repeater {
model: ScriptModel {
values: TrayService.unpinnedItems
onValuesChanged: {
root.updateAnchor();
if (values.length === 0) {
root.close();
}
}
}
delegate: TrayButton {
id: trayButton
required property var modelData
item: modelData
topInset: 0
bottomInset: 0
implicitWidth: 40
implicitHeight: 40
colBackground: ColorUtils.transparentize(Looks.colors.bg2)
colBackgroundHover: Looks.colors.bg2Hover
colBackgroundActive: Looks.colors.bg2Active
onMenuOpenChanged: {
// The overflow menu should only be closed when the user clicks outside
// However the focus grab refuses to reactivate, so we can't have that
// But most of the time the user dismisses the menu by clicking outside anyway,
// so this is acceptable.
if (!menuOpen) {
root.close();
}
}
property real initialX
property real initialY
Behavior on x {
animation: Looks.transition.move.createObject(this)
}
Behavior on y {
animation: Looks.transition.move.createObject(this)
}
MouseArea {
id: dragArea
anchors.fill: parent
drag.target: parent
drag.threshold: 2
onPressed: event => {
trayButton.Drag.hotSpot.x = event.x;
trayButton.Drag.hotSpot.y = event.y;
trayButton.initialX = trayButton.x;
trayButton.initialY = trayButton.y;
trayButton.Drag.active = true;
}
onReleased: {
if (!dragArea.drag.active) {
trayButton.click();
} else {
if (!unpinDropArea.containsDrag && unpinDropArea.willUnpin) {
// Quickshell would crash if we don't hide this item first. Took me fucking 3 hours to figure out...
trayButton.visible = false;
TrayService.togglePin(trayButton.item.id);
unpinDropArea.willUnpin = false;
} else {
trayButton.x = trayButton.initialX;
trayButton.y = trayButton.initialY;
}
}
trayButton.Drag.active = false;
}
}
}
}
}
DropArea {
id: unpinDropArea
anchors.fill: parent
property bool willUnpin: false
onEntered: willUnpin = false
onExited: willUnpin = true
}
}
}
@@ -6,6 +6,7 @@ import qs.modules.waffle.looks
Kirigami.Icon {
id: root
required property string icon
property alias monochrome: root.isMask
// Should be 16, but it appears the icons have some padding,
// Unlike the Windows-only Segoe UI icons, the open source FluentUI ones are hella small
property int implicitSize: 20
@@ -17,9 +17,12 @@ Singleton {
property real backgroundTransparency: 0.17
property real contentTransparency: 0.25
property real shadowTransparency: 0.6
colors: QtObject {
id: colors
property color ambientShadow: ColorUtils.transparentize("#000000", 0.75)
property color bgPanelFooter: root.dark ? "#1C1C1C" : "#EEEEEE"
property color bgPanelBody: root.dark ? "#242424" : "#F2F2F2"
property color bgPanelSeparator: root.dark ? "#191919" : "#E0E0E0"
property color bg0: root.dark ? "#1C1C1C" : "#EEEEEE"
property color bg0Border: root.dark ? "#404040" : "#BEBEBE"
property color bg1: root.dark ? "#2C2C2C" : "#F7F7F7"
@@ -34,6 +37,7 @@ Singleton {
property color fg1: root.dark ? "#D1D1D1" : "#626262"
property color danger: "#C42B1C"
property color dangerActive: "#B62D1F"
property color warning: "#FF9900"
// property color accent: root.dark ? "#A5C6D8" : "#5377A3"
property color accent: Appearance.m3colors.m3primary
property color accentUnfocused: root.dark ? "#989898" : "#848484"
@@ -61,7 +65,7 @@ Singleton {
}
property QtObject pixelSize: QtObject {
property real normal: 11
property real large: 15
property real large: 14
}
}
@@ -115,6 +119,14 @@ Singleton {
}
}
property Component rotate: Component {
NumberAnimation {
duration: 170
easing.type: Easing.BezierSpline
easing.bezierCurve: transition.easing.bezierCurve.easeInOut
}
}
property Component anchor: Component {
AnchorAnimation {
duration: 160
@@ -0,0 +1,25 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Rectangle {
id: root
required property var target
z: 0
anchors {
fill: target
margins: -border.width
}
border.color: Looks.colors.ambientShadow
border.width: 1
color: "transparent"
radius: target.radius + border.width
}
@@ -0,0 +1,79 @@
import QtQuick
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
Item {
id: root
signal closed()
property alias border: borderRect
required default property Item contentItem
property real visualMargin: 12
function close() {
closeAnim.start();
}
readonly property bool barAtBottom: Config.options.waffles.bar.bottom
implicitHeight: borderRect.implicitHeight
implicitWidth: borderRect.implicitWidth
Rectangle {
id: borderRect
color: "transparent"
radius: Looks.radius.large
border.color: Looks.colors.bg2Border
border.width: 1
implicitWidth: contentItem.implicitWidth + border.width * 2
implicitHeight: contentItem.implicitHeight + border.width * 2
children: [root.contentItem]
anchors {
left: parent.left
right: parent.right
top: root.barAtBottom ? undefined : parent.top
bottom: root.barAtBottom ? parent.bottom : undefined
// Opening anim
bottomMargin: root.barAtBottom ? sourceEdgeMargin : 0
topMargin: root.barAtBottom ? 0 : sourceEdgeMargin
}
Component.onCompleted: {
openAnim.start();
}
property real sourceEdgeMargin: -(implicitHeight + root.visualMargin)
PropertyAnimation {
id: openAnim
target: borderRect
property: "sourceEdgeMargin"
to: 0
duration: 200
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
}
SequentialAnimation {
id: closeAnim
PropertyAnimation {
target: borderRect
property: "sourceEdgeMargin"
to: -(implicitHeight + root.visualMargin)
duration: 150
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
}
ScriptAction {
script: {
root.closed();
}
}
}
}
}
@@ -0,0 +1,95 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
// Generic button with background
Button {
id: root
property color colBackgroundHover: Looks.colors.bg2Hover
property color colBackgroundActive: Looks.colors.bg2Active
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
property alias monochromeIcon: buttonIcon.monochrome
property bool forceShowIcon: false
property var altAction: () => {}
property var middleClickAction: () => {}
property real inset: 2
topInset: inset
bottomInset: inset
leftInset: inset
rightInset: inset
horizontalPadding: 10
verticalPadding: 6
implicitHeight: contentItem.implicitHeight + verticalPadding * 2
implicitWidth: contentItem.implicitWidth + horizontalPadding * 2
background: Rectangle {
radius: Looks.radius.medium
color: {
if (root.down) {
return root.colBackgroundActive;
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover;
} else {
return root.colBackground;
}
}
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton | Qt.MiddleButton
onClicked: (event) => {
if (event.button === Qt.LeftButton) root.clicked();
if (event.button === Qt.RightButton) root.altAction();
if (event.button === Qt.MiddleButton) root.middleClickAction();
}
}
contentItem: Item {
anchors {
fill: parent
margins: root.inset
}
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
RowLayout {
id: contentLayout
anchors {
fill: parent
leftMargin: root.horizontalPadding
rightMargin: root.horizontalPadding
}
spacing: 12
FluentIcon {
id: buttonIcon
visible: root.icon.name !== "" || root.forceShowIcon
monochrome: true
implicitSize: 16
Layout.leftMargin: 6
Layout.fillWidth: false
Layout.alignment: Qt.AlignVCenter
icon: root.icon.name
}
WText {
Layout.rightMargin: 12
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft
text: root.text
horizontalAlignment: Text.AlignLeft
font {
pixelSize: Looks.font.pixelSize.large
}
}
}
}
}
@@ -0,0 +1,32 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Button {
id: root
implicitHeight: 36
property color colBackground: ColorUtils.transparentize(Looks.colors.bg1)
property color colBackgroundHover: Looks.colors.bg1Hover
property color colBackgroundActive: Looks.colors.bg1Active
property color color
property color colForeground: Looks.colors.fg
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
background: Rectangle {
radius: Looks.radius.medium
color: root.color
}
}
@@ -10,42 +10,37 @@ import qs.modules.waffle.looks
PopupToolTip {
id: root
property real padding: 2
verticalPadding: padding
horizontalPadding: padding
property Item realContentItem
realContentItem: WText {
text: root.text
anchors.centerIn: parent
}
property real visualMargin: 11
verticalPadding: visualMargin
horizontalPadding: visualMargin
property real realContentVerticalPadding: 8
property real realContentHorizontalPadding: 10
contentItem: Item {
anchors.centerIn: parent
implicitWidth: realContent.implicitWidth + root.verticalPadding * 2
implicitHeight: realContent.implicitHeight + root.horizontalPadding * 2
implicitWidth: realContent.implicitWidth + 2 * 2
implicitHeight: realContent.implicitHeight + 2 * 2
Rectangle {
id: ambientShadow
z: 0
anchors {
fill: realContent
margins: -border.width
}
border.color: ColorUtils.transparentize(Looks.colors.bg0Border, Looks.shadowTransparency)
border.width: 1
color: "transparent"
radius: realContent.radius + border.width
WAmbientShadow {
target: realContent
}
Rectangle {
id: realContent
z: 1
anchors.centerIn: parent
implicitWidth: tooltipText.implicitWidth + 10 * 2
implicitHeight: tooltipText.implicitHeight + 8 * 2
implicitWidth: root.realContentItem.implicitWidth + root.realContentHorizontalPadding * 2
implicitHeight: root.realContentItem.implicitHeight + root.realContentVerticalPadding * 2
color: Looks.colors.bg1
radius: Looks.radius.medium
WText {
id: tooltipText
text: root.text
anchors.centerIn: parent
}
children: [root.realContentItem]
}
}
}
@@ -135,10 +135,18 @@ Singleton {
}
function syncBrightness() {
const brightnessValue = Math.max(monitor.multipliedBrightness, 0)
const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1);
setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded] : ["brightnessctl", "--class", "backlight", "s", rawValueRounded, "--quiet"];
setProc.startDetached();
const brightnessValue = Math.max(monitor.multipliedBrightness, 0);
if (isDdc) {
const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1);
setProc.command = ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded];
setProc.startDetached();
} else {
const valuePercentNumber = Math.floor(brightnessValue * 100);
let valuePercent = `${valuePercentNumber}%`;
if (valuePercentNumber == 0) valuePercent = "1"; // Prevent fully black
setProc.command = ["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"];
setProc.startDetached();
}
}
function setBrightness(value: real): void {
@@ -30,7 +30,7 @@ Singleton {
function enable() {
root.active = true
Quickshell.execDetached(["bash", "-c", "easyeffects --gapplication-service || flatpak run com.github.wwmm.easyeffects --gapplication-service"])
Quickshell.execDetached(["bash", "-c", "easyeffects --hide-window --service-mode || flatpak run com.github.wwmm.easyeffects --hide-window --service-mode"])
}
function toggle() {
@@ -58,6 +58,7 @@ Singleton {
// Update the distroIcon property based on distroId
switch (distroId) {
case "artix":
case "arch": distroIcon = "arch-symbolic"; break;
case "endeavouros": distroIcon = "endeavouros-symbolic"; break;
case "cachyos": distroIcon = "cachyos-symbolic"; break;
@@ -8,6 +8,14 @@ import Quickshell.Wayland
Singleton {
id: root
function togglePin(appId) {
if (Config.options.dock.pinnedApps.indexOf(appId) !== -1) {
Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.filter(id => id !== appId)
} else {
Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.concat([appId])
}
}
property list<var> apps: {
var map = new Map();
@@ -0,0 +1,45 @@
pragma Singleton
import qs.modules.common
import QtQuick
import Quickshell
import Quickshell.Services.SystemTray
Singleton {
id: root
property bool smartTray: Config.options.tray.filterPassive
property list<var> itemsInUserList: SystemTray.items.values.filter(i => (Config.options.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive)))
property list<var> itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.tray.pinnedItems.includes(i.id) && (!smartTray || i.status !== Status.Passive)))
property bool invertPins: Config.options.tray.invertPinnedItems
property list<var> pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList
property list<var> unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList
function getTooltipForItem(item) {
var result = item.tooltipTitle.length > 0 ? item.tooltipTitle
: (item.title.length > 0 ? item.title : item.id);
if (item.tooltipDescription.length > 0) result += " • " + item.tooltipDescription;
if (Config.options.tray.showItemId) result += "\n[" + item.id + "]";
return result;
}
// Pinning
function pin(itemId) {
var pins = Config.options.tray.pinnedItems;
if (pins.includes(itemId)) return;
Config.options.tray.pinnedItems.push(itemId);
}
function unpin(itemId) {
Config.options.tray.pinnedItems = Config.options.tray.pinnedItems.filter(id => id !== itemId);
}
function togglePin(itemId) {
var pins = Config.options.tray.pinnedItems;
if (pins.includes(itemId)) {
unpin(itemId)
} else {
pin(itemId)
}
}
}
@@ -0,0 +1,57 @@
pragma Singleton
import qs.modules.common
import qs.modules.common.functions
import QtQuick
import Quickshell
import Quickshell.Io
/*
* System updates service. Currently only supports Arch.
*/
Singleton {
id: root
property bool available: false
property int count: 0
readonly property bool updateAdvised: available && count > Config.options.updates.adviseUpdateThreshold
readonly property bool updateStronglyAdvised: available && count > Config.options.updates.stronglyAdviseUpdateThreshold
function load() {}
function refresh() {
if (!available) return;
print("[Updates] Checking for system updates")
checkUpdatesProc.running = true;
}
Timer {
interval: Config.options.updates.checkInterval * 60 * 1000
repeat: true
running: Config.ready
onTriggered: {
print("[Updates] Periodic update check due")
root.refresh();
}
}
Process {
id: checkAvailabilityProc
running: true
command: ["which", "checkupdates"]
onExited: (exitCode, exitStatus) => {
root.available = (exitCode === 0);
root.refresh();
}
}
Process {
id: checkUpdatesProc
command: ["bash", "-c", "checkupdates | wc -l"]
stdout: StdioCollector {
onStreamFinished: {
root.count = parseInt(text.trim());
}
}
}
}
+4 -1
View File
@@ -28,6 +28,7 @@ import qs.modules.ii.overlay
import qs.modules.ii.verticalBar
import qs.modules.ii.wallpaperSelector
import qs.modules.waffle.actionCenter
import qs.modules.waffle.background
import qs.modules.waffle.bar
@@ -49,6 +50,7 @@ ShellRoot {
ConflictKiller.load()
Cliphist.refresh()
Wallpapers.load()
Updates.load()
}
// Load enabled stuff
@@ -74,6 +76,7 @@ ShellRoot {
PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} }
PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} }
PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} }
PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} }
PanelLoader { identifier: "wBar"; component: WaffleBar {} }
PanelLoader { identifier: "wBackground"; component: WaffleBackground {} }
@@ -87,7 +90,7 @@ ShellRoot {
property list<string> families: ["ii", "waffle"]
property var panelFamilies: ({
"ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"],
"waffle": ["wBar", "wBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiWallpaperSelector"],
"waffle": ["wBar", "wBackground", "wActionCenter", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiSessionScreen", "iiSidebarRight", "iiWallpaperSelector"],
})
function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily)
+1
View File
@@ -211,6 +211,7 @@ Extra dependencies.
- `qt6-translations`
- `qt6-virtualkeyboard`
- `qt6-wayland`
- `kirigami`
- `kdialog`
- `syntax-highlighting`
@@ -9,7 +9,7 @@ conflicts=("quickshell-git")
_pkgname=quickshell
pkgname="$_prefix-$_pkgname-git"
pkgver=0.1.0.r1
pkgrel=5
pkgrel=6
pkgdesc="$_pkgname-git pinned commit and extra deps for $_prefix"
arch=(x86_64 aarch64)
url='https://git.outfoxxed.me/quickshell/quickshell'
@@ -39,6 +39,7 @@ depends=(
qt6-translations
qt6-virtualkeyboard
qt6-wayland
kirigami
kdialog
syntax-highlighting
)
@@ -1,36 +0,0 @@
Name: Bibata-Modern-Classic
Version: 2.0.7
Release: %autorelease
Summary: Open source, compact, and material designed cursor set.
License: GPL-3.0
URL: https://github.com/ful1e5/Bibata_Cursor
Source0: %{name}.tar.xz
BuildArch: noarch
%description
Open source, compact, and material designed cursor set.
%prep
wget --content-disposition -q -N -P %{_sourcedir} %{url}/releases/download/v%{version}/Bibata-Modern-Classic.tar.xz
wget -q -O %{_buildrootdir}/LICENSE %{url}/raw/refs/heads/main/LICENSE
%setup -q -n %{name}
%build
:
%install
install -d -m 0755 %{buildroot}%{_iconsdir}/Bibata-Modern-Classic
cp -r * %{buildroot}%{_iconsdir}/Bibata-Modern-Classic
install -d -m 0755 %{buildroot}%{_licensedir}/%{name}
install -m 0644 %{_buildrootdir}/LICENSE %{buildroot}%{_licensedir}/%{name}/
%files
%{_iconsdir}/Bibata-Modern-Classic
%license %{_licensedir}/%{name}/LICENSE
%changelog
%autochangelog
@@ -1,42 +0,0 @@
Name: JetBrainsMonoNerdFont-Regular
Version: 1.2
Release: %autorelease
Summary: JetBrainsMonoNerdFonts (TrueType Outlines) (Regulear)
License: MIT
URL: https://github.com/Zhaopudark/JetBrainsMonoNerdFonts
Source0: %{name}
BuildRequires: fonts-rpm-macros
BuildArch: noarch
%description
An auto-updated compiling version of JetBrains Mono that has been patched with Nerd Fonts.
%prep
wget --content-disposition -q -N -P %{_sourcedir} %{url}/releases/download/v%{version}/JetBrainsMonoNerdFont-Regular-v%{version}.ttf
wget -q -P %{_sourcedir} %{url}/raw/refs/heads/main/LICENSE
%build
:
%install
install -d -m 0755 %{buildroot}%{_fontdir}
install -m 0644 %{_sourcedir}/JetBrainsMonoNerdFont*ttf %{buildroot}%{_fontdir}
install -d -m 0755 %{buildroot}%{_licensedir}/%{name}
install -m 0644 %{_sourcedir}/LICENSE %{buildroot}%{_licensedir}/%{name}/
%post
/usr/bin/fc-cache -fv >/dev/null 2>&1 || :
%postun
/usr/bin/fc-cache -fv >/dev/null 2>&1 || :
%files
%{_fontdir}/JetBrainsMonoNerdFont*ttf
%license %{_licensedir}/%{name}/LICENSE
%changelog
%autochangelog
-50
View File
@@ -1,50 +0,0 @@
%global commit0 0e3707f6dafebb121d98b53c64364d16fefe481d
%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
%global bumpver 100
Name: MicroTeX
Version: 0.0.1%{?bumpver:^%{bumpver}.git%{shortcommit0}}
Release: %autorelease
Summary: A dynamic, cross-platform, and embeddable LaTeX rendering library
License: MIT
URL: https://github.com/NanoMichael/MicroTeX
Source0: %{name}-%{shortcommit0}.tar.gz
BuildRequires: gcc-c++ cmake
BuildRequires: pkgconfig(tinyxml2)
BuildRequires: gtkmm3.0-devel gtksourceviewmm3-devel cairomm-devel
%description
MicroTeX is a library for rendering LaTeX mathematical formulas, supporting multiple backends
such as GTK+, Qt, and Skia. It provides both library components and demo applications for
testing LaTeX rendering.
%prep
curl -fsSL --retry 3 \
https://codeload.github.com/NanoMichael/MicroTeX/tar.gz/%{shortcommit0} \
-o %{_sourcedir}/%{name}-%{shortcommit0}.tar.gz
%setup -q -n %{name}-%{shortcommit0}
%build
mkdir -p build
cd build
cmake ..
make -j$(nproc)
%install
mkdir -p %{buildroot}/opt/MicroTeX
cp build/LaTeX %{buildroot}/opt/MicroTeX/
cp -r build/res %{buildroot}/opt/MicroTeX/
install -Dpm 0644 LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE
install -Dpm 0644 res/greek/LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE-greek
install -Dpm 0644 res/cyrillic/LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE-cyrillic
%files
/opt/MicroTeX/
%license %{_licensedir}/%{name}/
%changelog
%autochangelog
-38
View File
@@ -1,38 +0,0 @@
Name: breeze-plus
Version: 6.19.0
Release: %autorelease
Summary: Breeze theme with additional icons
License: LGPL-2.1
URL: https://github.com/mjkim0727/breeze-plus
Source0: %{name}-%{version}.tar.gz
BuildArch: noarch
%description
Breeze icon theme with additional icons for applications not covered by the
official Breeze theme. Includes icons for Wine, third-party apps, and more.
%prep
wget --content-disposition -q -N -P %{_sourcedir} %{url}/archive/refs/tags/%{version}.tar.gz
%setup -q
%build
:
%install
install -d -m 0755 %{buildroot}%{_iconsdir}/breeze-plus
cp -r src/breeze-plus %{buildroot}%{_iconsdir}/
cp -r src/breeze-plus-dark %{buildroot}%{_iconsdir}/
install -d -m 0755 %{buildroot}%{_licensedir}/%{name}
install -m 0644 LICENSE %{buildroot}%{_licensedir}/%{name}/
%files
%{_iconsdir}/breeze-plus/
%{_iconsdir}/breeze-plus-dark/
%license %{_licensedir}/%{name}/LICENSE
%changelog
%autochangelog

Some files were not shown because too many files have changed in this diff Show More