Merge branch 'main' of github.com:Teoro01/hyprland-quickshell-gui

This commit is contained in:
Teo
2026-03-08 14:13:46 +01:00
23 changed files with 1676 additions and 136 deletions
+30
View File
@@ -0,0 +1,30 @@
# HyprlandQuickshellGUI: a replacement for Waybar
## Colors
### [Matugen](https://github.com/InioX/matugen)
We use [matugen](https://github.com/InioX/matugen) for the colors. With these Templates for Some Applications we use: [maguten-config](https://git.honney333.work/honney/matugen-config)
## Wallpaper
We use a custom directory by deafault to save all your Wallpapers:
```
$HOME/Pictures/Wallpapers/
```
### Compatible Wallpaper Software:
- [hyprpaper](https://github.com/hyprwm/hyprpaper)
- [swww](https://github.com/LGFae/swww)
- [awww (comming)](https://codeberg.org/LGFae/awww)
### Recommended Software for some stuff:
- [ImageMagick (resizing & blurring)](https://github.com/ImageMagick/ImageMagick)
## Other Software:
### Logout:
- [wlogout](https://github.com/ArtsyMacaw/wlogout)
- [nmcli (Recommended for Wifi)](https://github.com/ushiboy/nmcli)
## TO Enable Sub Programs:
Add the Lines to your ```~/.config/hypr/hyprland.conf``` keep in mind that you might need to change the Keybind.
### Wallpaper Picker
```bind = $mainMod SHIFT, W, global, quickshell:Wallpaper```
### JLearner
```bind = $mainMod SHIFT, J, global, quickshell:JLearner```
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 MiB

+57
View File
@@ -0,0 +1,57 @@
pragma Singleton
import QtQuick 2.15
QtObject {
// ===== PRIMARY =====
readonly property color primary: "#cbcb77"
readonly property color primary_fg: "#323200"
readonly property color primaryContainer: "#494900"
readonly property color primaryContainer_fg: "#e7e790"
// ===== SECONDARY =====
readonly property color secondary: "#cac8a5"
readonly property color secondary_fg: "#323218"
readonly property color secondaryContainer: "#48482d"
readonly property color secondaryContainer_fg: "#e6e4bf"
// ===== TERTIARY =====
readonly property color tertiary: "#a4d0bd"
readonly property color tertiary_fg: "#0a372a"
readonly property color tertiaryContainer: "#254e40"
readonly property color tertiaryContainer_fg: "#bfecd9"
// ===== ERROR =====
readonly property color error: "#ffb4ab"
readonly property color error_fg: "#690005"
readonly property color errorContainer: "#93000a"
readonly property color errorContainer_fg: "#ffdad6"
// ===== BACKGROUND / SURFACE =====
readonly property color background: "#14140c"
readonly property color background_fg: "#e6e3d5"
readonly property color surface: "#14140c"
readonly property color surface_fg: "#e6e3d5"
readonly property color surfaceVariant: "#48473a"
readonly property color surfaceVariant_fg: "#c9c7b6"
// ===== SURFACE CONTAINERS =====
readonly property color surfaceContainerLowest: "#0f0f07"
readonly property color surfaceContainerLow: "#1c1c14"
readonly property color surfaceContainer: "#202018"
readonly property color surfaceContainerHigh: "#2b2a22"
readonly property color surfaceContainerHighest: "#36352c"
// ===== OUTLINE / UTILITY =====
readonly property color outline: "#939182"
readonly property color outlineVariant: "#48473a"
readonly property color shadowColor: "#000000"
readonly property color scrimColor: "#000000"
// ===== INVERSE =====
readonly property color inverseSurface: "#e6e3d5"
readonly property color inverseSurface_fg: "#313128"
readonly property color inversePrimary: "#616219"
}
+14
View File
@@ -0,0 +1,14 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.928 4.59635C15.656 4.59154 15.3937 4.6962 15.2018 4.88607L14.7443 5.33897L14.7532 5.3477L10.6335 9.40399C10.3409 9.69209 10.3409 10.1592 10.6335 10.4472L12.8293 12.6093C13.1219 12.8974 13.5962 12.8974 13.8888 12.6093L18.0198 8.54183L18.0206 8.54261L20.7408 5.93893C21.2184 5.48177 20.898 4.68415 20.232 4.6724L15.928 4.59635Z" fill="url(#paint0_linear_451_3032)"/>
<path d="M6.07245 9.17448C6.34442 9.16967 6.6067 9.27425 6.79855 9.4642L7.25611 9.91706L7.24725 9.92579L11.3669 13.9821C11.6595 14.2702 11.6595 14.7373 11.3669 15.0254L9.17115 17.1873C8.87858 17.4754 8.40417 17.4754 8.11157 17.1873L3.98062 13.1199L3.9798 13.1207L1.25963 10.5171C0.782021 10.0599 1.10244 9.26227 1.7684 9.25053L6.07245 9.17448Z" fill="url(#paint1_linear_451_3032)"/>
<defs>
<linearGradient id="paint0_linear_451_3032" x1="13.3523" y1="13.0948" x2="3.69727" y2="3.70258" gradientUnits="userSpaceOnUse">
<stop stop-color="#2AD454"/>
<stop offset="1" stop-color="#B3FF5A"/>
</linearGradient>
<linearGradient id="paint1_linear_451_3032" x1="13.3518" y1="13.0948" x2="3.69684" y2="3.70247" gradientUnits="userSpaceOnUse">
<stop stop-color="#2AD454"/>
<stop offset="1" stop-color="#B3FF5A"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+24
View File
@@ -0,0 +1,24 @@
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_451_2160)">
<path d="M20.2812 11C20.2812 5.87599 16.124 1.71875 11 1.71875C5.87599 1.71875 1.71875 5.87599 1.71875 11C1.71875 16.124 5.87599 20.2812 11 20.2812C16.124 20.2812 20.2812 16.124 20.2812 11Z" stroke="url(#paint0_linear_451_2160)" stroke-width="2.6" stroke-miterlimit="10"/>
<path d="M14.0938 14.0938L7.90625 7.90625" stroke="url(#paint1_linear_451_2160)" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7.90625 14.0938L14.0938 7.90625" stroke="url(#paint2_linear_451_2160)" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<linearGradient id="paint0_linear_451_2160" x1="20.2263" y1="1.83639" x2="1.81064" y2="20.0988" gradientUnits="userSpaceOnUse">
<stop stop-color="#FE7970"/>
<stop offset="1" stop-color="#DC3282"/>
</linearGradient>
<linearGradient id="paint1_linear_451_2160" x1="14.0754" y1="7.94547" x2="7.93688" y2="14.0329" gradientUnits="userSpaceOnUse">
<stop stop-color="#FE7970"/>
<stop offset="1" stop-color="#DC3282"/>
</linearGradient>
<linearGradient id="paint2_linear_451_2160" x1="14.0754" y1="7.94547" x2="7.93688" y2="14.0329" gradientUnits="userSpaceOnUse">
<stop stop-color="#FE7970"/>
<stop offset="1" stop-color="#DC3282"/>
</linearGradient>
<clipPath id="clip0_451_2160">
<rect width="22" height="22" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

-3
View File
@@ -77,8 +77,5 @@ PanelWindow {
PowerButton {} PowerButton {}
} }
// TODO: Somewhere add Tray
} }
} }
+127
View File
@@ -0,0 +1,127 @@
#!/bin/bash
ID="$1"
if [[ -z "$ID" ]]; then
echo "Usage: $0 Number"
exit 1
fi
# Command Helper: hyprctl clients -j | jq '.[] | select(.workspace.id == 1)'
# Icons: https://www.nerdfonts.com/cheat-sheet
declare -A MAP=(
# Youtube
["Grayjay"]=""
# Browser
["Ablaze Floorp"]=""
["LibreWolf"]=""
["midori"]=""
["brave-browser"]=""
["chromium"]=""
# E-Mail
["org.mozilla.Thunderbird"]="󰇮"
# Terminals
["kitty"]=""
["Alacritty"]=""
# Videoplayer
["VLC media player"]="󰕼"
["mpv"]=""
["ffplay"]=""
# Chats
["discord"]=""
["vesktop"]=""
["Signal"]="󰭹"
["xyz.chatboxapp.app"]=""
# Coding
["Visual Studio Code"]=""
["VSCodium"]=""
["jetbrains-idea"]=""
["jetbrains-studio"]=""
# Text Editor
["mousepad"]="󱩼"
# Password
["org.keepassxc.KeePassXC"]=""
["Yubico Authenticator"]=""
# Cloud
["com.nextcloud.desktopclient.nextcloud"]="󰅟"
# App Store
["Octopi"]="󰒚"
# Games
["steam"]=""
["org.prismlauncher.PrismLauncher"]="󰍳"
["Minecraft* 1.21.11"]="󰍳"
["game"]=""
# Digital Arts
["Blender"]=""
["Krita"]=""
# VM
["virt-manager"]=""
["VirtualBox Manager"]=""
["remote-viewer"]=""
# Audio
["qpwgraph"]="󱡫"
["Carla"]="󱡫"
# Video
["com.obsproject.Studio"]="󰨜"
["hyprland-share-picker"]="󱎴"
# Document Editor/Viewer
["libreoffice-startcenter"]=""
["libreoffice-base"]=""
["libreoffice-calc"]=""
["libreoffice-draw"]=""
["libreoffice-impress"]=""
["libreoffice-math"]=""
["libreoffice-writer"]=""
["com.github.flxzt.rnote"]="󱩼"
["com.github.xournalpp.xournalpp"]="󱩼"
["qpdfview.local.qpdfview"]="󱩼"
# Hardware
["Mission Center"]=""
["QDirStat"]="󰋊"
["Disks"]="󰋊"
# File Manager
["nemo"]="󰉋"
["org.gnome.Nautilus"]="󰉋"
["org.kde.dolphin"]="󰉋"
["xdg-desktop-portal-gtk"]="󰉋"
# Settings
["nwg-look"]="" # gtk-settings
["qt5ct"]=""
["qt6ct"]=""
["sddm-conf"]=""
["blueman-manager"]="󰂯"
["nm-connection-editor"]="󱛆"
# Calculator
["Calculator"]="󰪚"
# VPN
["Proton VPN"]="󰌾"
)
# APPS=$(hyprctl clients -j | jq -r ".[] | select(.workspace.id == $ID) | .initialTitle")
mapfile -t APPS < <(
hyprctl clients -j |
jq -r ".[] | select(.workspace.id == $ID) | .initialTitle"
)
mapfile -t APPS_CLASS < <(
hyprctl clients -j |
jq -r ".[] | select(.workspace.id == $ID) | .initialClass"
)
for i in "${!APPS[@]}"; do
if [[ -n "${MAP["${APPS[$i]}"]}" ]]; then
APPS[$i]="${MAP["${APPS[$i]}"]}"
elif [[ -n "${MAP["${APPS_CLASS[$i]}"]}" ]]; then
APPS[$i]="${MAP["${APPS_CLASS[$i]}"]}"
else
APPS[$i]="${APPS_CLASS[$i]}|${APPS[$i]}"
fi
done
# for app in "${APPS[@]}"; do
# echo "$app"
# done
RESULT="$ID $(printf '%s ' "${APPS[@]}")"
RESULT=${RESULT% } # remove trailing space
echo "$RESULT"
+57 -18
View File
@@ -4,34 +4,73 @@ import Quickshell.Hyprland
import "../../../colors" import "../../../colors"
Rectangle { //workspace Rectangle { //workspace
width: timeDisplay.width + 16 width: timeDisplay.width + 8 + dateDisplay.width + 16
height: timeDisplay.height + 16 height: timeDisplay.height + 16
radius: 4 radius: 4
color: Colors.primaryContainer color: Colors.primaryContainer
border.color: Colors.primaryContainer_fg border.color: Colors.primaryContainer_fg
border.width: 2 border.width: 2
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
Text { Row {
id: timeDisplay id: row
spacing: 8
anchors.centerIn: parent
property string currentTime: "" Text {
id: dateDisplay
property string currentDate: ""
text: currentTime text: currentDate
color: Colors.primary color: Colors.primary
font.pixelSize: 18 font.pixelSize: 18
font.family: "Inter, sans-serif" font.family: "Inter, sans-serif"
anchors.centerIn: parent Timer {
id: dateTimer
//Update time every second interval: 60000
Timer { running: false
interval: 1000 repeat: true
running: true triggeredOnStart: true
repeat: true onTriggered: {
onTriggered: { var now = new Date()
var now = new Date() dateDisplay.currentDate = Qt.formatDate(now, "dd.MM.yyyy")
timeDisplay.currentTime = Qt.formatTime(now, "hh:mm:ss") }
}
} }
Text {
id: timeDisplay
property string currentTime: ""
text: currentTime
color: Colors.primary
font.pixelSize: 18
font.family: "Inter, sans-serif"
Component.onCompleted: {
var now = new Date()
currentTime = Qt.formatTime(now, "hh:mm:ss")
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var now = new Date()
timeDisplay.currentTime = Qt.formatTime(now, "hh:mm:ss")
}
}
}
}
onClicked: {
dateDisplay.currentDate = "";
dateTimer.running = !dateTimer.running;
} }
} }
} }
+5 -3
View File
@@ -6,7 +6,7 @@ import "../../../colors"
MouseArea { MouseArea {
id: networkButton id: networkButton
width: output.width + 16 width: bar.height
height: bar.height height: bar.height
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
@@ -19,7 +19,9 @@ MouseArea {
anchors.fill: parent anchors.fill: parent
color: networkButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25) color: networkButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25)
radius: 6 radius: 6
width: output.width + 16 border.width: 2
border.color: networkButton.containsMouse ? Colors.tertiaryContainer_fg
: Colors.secondaryContainer_fg
Text { Text {
id: output id: output
@@ -37,7 +39,7 @@ MouseArea {
stdout: StdioCollector { stdout: StdioCollector {
onStreamFinished: { onStreamFinished: {
output.text = this.text.trim() output.text = this.text.trim()
console.log("networkScript output:", this.text.trim()) // console.log("networkScript output:", this.text.trim())
} }
} }
} }
+130 -70
View File
@@ -7,85 +7,145 @@ import "../../../colors"
Row { // Workspace Row Row { // Workspace Row
id: trayRow id: trayRow
spacing: 8 spacing: 4
Repeater { Repeater {
model: SystemTray.items model: SystemTray.items
Column{
Rectangle { //trayItem Rectangle{
id: trayItem width: iconImage.width + 8
width: bar.height height: (bar.height-trayItem.height)/2
height: bar.height color: "transparent"
radius: 4
color: "transparent"
border.width: 2
Process {
id: steamRunner
command: ["steam", "steam://open/main"]
running: false
} }
Rectangle { //trayItem
id: trayItem
width: iconImage.width + 8
height: iconImage.height + 8
radius: 4
color: "transparent"
border.width: 2
MouseArea { Process {
id: workspaceButton id: steamRunner
hoverEnabled: true command: ["steam", "steam://open/main"]
cursorShape: Qt.PointingHandCursor running: false
anchors.fill: parent }
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: (mouse) => { MouseArea {
switch (mouse.button) { id: workspaceButton
case Qt.LeftButton: hoverEnabled: true
if (!modelData.onlyMenu) { cursorShape: Qt.PointingHandCursor
modelData.activate() anchors.fill: parent
console.log('Activcate function of ', modelData.title) acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
onClicked: (mouse) => {
switch (mouse.button) {
case Qt.LeftButton:
if (!modelData.onlyMenu) {
modelData.activate()
console.log('Activcate function of ', modelData.title)
}
if (modelData.title.trim()==="Steam") {
steamRunner.startDetached()
}
break
case Qt.MiddleButton:
modelData.secondaryActivate()
break
// case Qt.RightButton:
// if (modelData.hasMenu) {
// console.log('Activcate menu of ', modelData.title)
// menuAnchor.open()
// }
// break
} }
if (modelData.title.trim()==="Steam") { }
steamRunner.startDetached()
} // QsMenuAnchor {
break // id: menuAnchor
case Qt.MiddleButton: // menu: modelData.menu
modelData.secondaryActivate() // anchor.window: menuHost
break // }
// case Qt.RightButton:
// if (modelData.hasMenu) { Rectangle {
// console.log('Activcate menu of ', modelData.title) anchors.fill: parent
// menuAnchor.open() color: workspaceButton.containsMouse ? Colors.tertiaryContainer
: modelData.active ? Colors.primaryContainer
: Colors.secondaryContainer
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
: modelData.active ? Colors.primaryContainer_fg
: Colors.secondaryContainer_fg
radius: 6
}
}
Image { // Image
id: iconImage
// source: modelData.icon; Does not work for some tray icons
fillMode: Image.PreserveAspectCrop
anchors.centerIn: parent
width: bar.height*0.5
height: bar.height*0.5
// Component.onCompleted: {
// // Default to the original icon
// let src = modelData.icon;
// // console.log("########## icon: " + modelData.icon)
// // Check if there is a "?" in the URL (custom path)
// if (src.includes('goxlr')) {
// let pathMatch = src.match(/path=([^&]+)/);
// if (pathMatch && pathMatch[1]) {
// let folderPath = pathMatch[1];
// // Build a file:// URL pointing to the actual icon file
// let segments = folderPath.split('/');
// let fileName = segments[segments.length - 1] + "-icon.png";
// console.log("########## filename: " + fileName)
// console.log("########## icon: " + modelData.icon)
// src = "file://" + pathMatch[1] + "/" + fileName;
// }
// } // }
// break
// // Set the resolved source
// iconImage.source = src;
// // console.log("Resolved icon source:", src);
// }
asynchronous: true
cache: true
}
Timer {
interval: 10000
running: true
repeat: true
triggeredOnStart: true
onTriggered: {
let src = modelData.icon;
// console.log("########## icon: " + modelData.icon)
// Check if there is a "?" in the URL (custom path)
if (src.includes('goxlr')) {
let pathMatch = src.match(/path=([^&]+)/);
if (pathMatch && pathMatch[1]) {
let folderPath = pathMatch[1];
// Build a file:// URL pointing to the actual icon file
let segments = folderPath.split('/');
let fileName = segments[segments.length - 1] + "-icon.png";
// console.log("########## filename: " + fileName)
// console.log("########## icon: " + modelData.icon)
src = "file://" + pathMatch[1] + "/" + fileName;
}
}
// Set the resolved source
iconImage.source = src;
} }
} }
// QsMenuAnchor {
// id: menuAnchor
// menu: modelData.menu
// anchor.window: menuHost
// }
Rectangle {
anchors.fill: parent
color: workspaceButton.containsMouse ? Colors.tertiaryContainer
: modelData.active ? Colors.primaryContainer
: Colors.secondaryContainer
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
: modelData.active ? Colors.primaryContainer_fg
: Colors.secondaryContainer_fg
radius: 6
}
}
Image { // Image
id: iconImage
source: modelData.icon
fillMode: Image.PreserveAspectCrop
anchors.centerIn: parent
width: bar.height*0.75
height: bar.height*0.75
asynchronous: true
cache: true
} }
} }
} }
+72 -35
View File
@@ -1,6 +1,7 @@
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Hyprland import Quickshell.Hyprland
import Quickshell.Io
import "../../../colors" import "../../../colors"
Row { // Workspace Row Row { // Workspace Row
@@ -9,48 +10,84 @@ Row { // Workspace Row
required property HyprlandMonitor thisMonitor required property HyprlandMonitor thisMonitor
spacing: 8 spacing: 8
// Get infos of Type Unknown
// Repeater {
// model: Hyprland.workspaces
// Rectangle {
// Component.onCompleted: {
// console.log("roles:")
// console.log("id:", modelData.id)
// console.log("monitor:", modelData.monitor)
// console.log("monitorId:", modelData.monitorId)
// console.log("name:", modelData.name)
// }
// }
// }
Repeater { // repeats workspaces Repeater { // repeats workspaces
model: Hyprland.workspaces model: Hyprland.workspaces
Loader {
active: modelData.monitor === thisMonitor
sourceComponent: Rectangle { //workspace
// visible: modelData.monitor === thisMonitor
width: (workspaceNum.width + 16) < (workspaceNum.height + 8) ? workspaceNum.height + 8 : workspaceNum.width + 16
height: workspaceNum.height + 8
radius: 4
color: workspaceButton.containsMouse ? Colors.tertiaryContainer
: modelData.active ? Colors.primaryContainer
: Colors.secondaryContainer
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
: modelData.active ? Colors.primaryContainer_fg
: Colors.secondaryContainer_fg
border.width: 2
Rectangle { //workspace MouseArea {
visible: modelData.monitor === thisMonitor id: workspaceButton
width: workspaceNum.width + 26 hoverEnabled: true
height: workspaceNum.height + 8 cursorShape: Qt.PointingHandCursor
radius: 4
color: workspaceButton.containsMouse ? Colors.tertiaryContainer
: modelData.active ? Colors.primaryContainer
: Colors.secondaryContainer
border.color: workspaceButton.containsMouse ? Colors.tertiaryContainer_fg
: modelData.active ? Colors.primaryContainer_fg
: Colors.secondaryContainer_fg
border.width: 2
MouseArea {
id: workspaceButton
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
anchors.fill: parent
onClicked: Hyprland.dispatch("workspace " + modelData.id)
Rectangle {
anchors.fill: parent anchors.fill: parent
color: "transparent" onClicked: Hyprland.dispatch("workspace " + modelData.id)
radius: 6
Rectangle {
anchors.fill: parent
color: "transparent"
radius: 6
}
}
Text {
id: workspaceNum
text: modelData.id
anchors.centerIn: parent
color: workspaceButton.containsMouse ? Colors.tertiary
: modelData.active ? Colors.primary
: Colors.secondary
font.pixelSize: 18
font.family: "Inter, sans-serif"
}
Process {
id: workspaceContent
command: ["sh", "-c", Quickshell.env("HOME") + "/.config/quickshell/modules/bar/scripts/Apps.sh " + modelData.id]
running: true
stdout: StdioCollector {
onStreamFinished: {
workspaceNum.text = this.text
// console.log("workspaceScript output:", this.text)
}
}
}
Timer {
interval: 1000
running: true
repeat: true
onTriggered: workspaceContent.running = true
} }
} }
Text {
id: workspaceNum
text: modelData.id
anchors.centerIn: parent
color: workspaceButton.containsMouse ? Colors.tertiary
: modelData.active ? Colors.primary
: Colors.secondary
font.pixelSize: 18
font.family: "Inter, sans-serif"
}
} }
} }
Text { Text {
+141
View File
@@ -0,0 +1,141 @@
import QtQuick
import Quickshell
import QtQuick.Controls
import Quickshell.Wayland
import Quickshell.Hyprland
import Quickshell.Io
import "../../colors"
import "./modules"
PanelWindow {
id: learner
implicitWidth: screen.width * 0.75
implicitHeight: screen.height * 0.5
color: "transparent"
visible: true
focusable: true
Component.onCompleted: {
semiJLoader.all_invisible()
semiJLoader.switchModeChooser()
}
FocusScope {
id: jLearnerScope
focus: true
Keys.onReleased: function(event) {
if (event.key === Qt.Key_Escape) {
console.log("Pressed Escape, closing JLearner")
jLearnerLoader.active = false
}
}
Rectangle {
anchors.fill: parent
color: "lightblue"
}
}
Rectangle {
anchors.fill: parent
radius: 10
color: Qt.alpha(Colors.surfaceContainerLowest, 0.5)
border.width: 2
border.color: Colors.outlineVariant
clip: true
Rectangle{
width: parent.width - 20
height: parent.height - 20
anchors.centerIn: parent
color: "transparent"
Column {
id: learnerWindow
anchors.fill: parent
spacing: 10
Rectangle {
id: head
width: parent.width
height: parent.height * 0.1
color: Colors.tertiaryContainer
radius: 10
border.width: 2
border.color: Colors.tertiaryContainer_fg
Text {
text: "Language Learner"
anchors.centerIn: parent
font.pixelSize: parent.height * 0.8
color: Colors.tertiary
}
}
Rectangle{
id: semiJLoader
width: parent.width
height: parent.height * 0.9 - parent.spacing
color: "transparent"
function all_invisible() {
languageChooser.visible = false
kanaVoicing.visible = false
voicingKana.visible = false
learnerResult.visible = false
}
function switchModeChooser() {
all_invisible()
languageChooser.visible = true
languageChooser.start()
}
function switch_KanaVoicing() {
all_invisible()
kanaVoicing.visible = true
kanaVoicing.start()
}
function switch_VoicingKana() {
all_invisible()
voicingKana.visible = true
voicingKana.start()
}
function switchResult(result, mode, results) {
all_invisible()
console.log("Result: " + result)
console.log("Mode: " + mode)
learnerResult.visible = true
learnerResult.result = result
learnerResult.mode = mode
learnerResult.results = results
learnerResult.start()
}
ModeChooser {
id: languageChooser
}
KanaVoicing {
id: kanaVoicing
}
VoicingKana {
id: voicingKana
}
JLearnerResult {
id: learnerResult
}
}
}
}
}
}
+29
View File
@@ -0,0 +1,29 @@
import QtQuick
import Quickshell
import Quickshell.Hyprland
import Quickshell.Io
Item { // container, invisible
id: jLoader
Loader {
id: jLearnerLoader
active: false
sourceComponent: JLearner { id: jLearner }
}
GlobalShortcut {
name: "JLearner"
description: "Open Japanese Learner"
onPressed: {
console.log("Pressed Meta+Shift+J (Hyprland global shortcut)")
jLearnerLoader.active = !jLearnerLoader.active
}
}
function closeJLearner() {
jLearnerLoader.active = false
}
}
+293
View File
@@ -0,0 +1,293 @@
import QtQuick
import QtQuick.Controls
import "../../../colors"
Item { // Chooser
id: learnerResult
width: parent.width
height: parent.height
anchors.margins: 15
visible: false
function switch_KanaVoicing() {
parent.switch_KanaVoicing()
}
function start() {
}
property real result: 0
property string mode: ""
property var results: createResults(10)
function createResults(n) {
var arr = []
for (var i = 0; i < n; i++) {
arr.push({
kana: "あ",
correctness: false,
answer: "a",
correct: "a"
})
}
return arr
}
Column {
anchors.fill: parent
spacing: 10
Rectangle {
id: learnerResultUpperText
color: Colors.primaryContainer
width: parent.width
height: parent.height * 0.1
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: mainText
text: "Ergebnisse:"
anchors.centerIn: parent
font.pixelSize: parent.height * 0.6
color: Colors.primary
}
}
Row {
width: parent.width
height: parent.height - learnerResultUpperText.height - parent.spacing
spacing: 10
Rectangle {
width: parent.width * 0.25
height: parent.height
color: "transparent"
Column{
width: parent.width
height: parent.height
spacing: 10
Repeater{
model: results
Row{
width: parent.width
height: parent.height/10 - parent.spacing * 0.9
spacing: 10
Rectangle{
width: parent.height
height: parent.height
color: Colors.primaryContainer
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text{
text: modelData.kana
width: parent.width
height: parent.height
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height * 0.125
anchors.horizontalCenterOffset: parent.width * 0.1
font.pixelSize: parent.height * 0.8
color: modelData.correctness ? "green" : "red"
}
}
Rectangle{
width: parent.height
height: parent.height
color: Colors.primaryContainer
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Image {
source: modelData.correctness ? "../../../colors/icons/correct.svg" : "../../../colors/icons/wrong.svg"
anchors.centerIn: parent
width: parent.height*0.8
height: parent.height*0.8
smooth: true
sourceSize.width: width
sourceSize.height: height
}
}
Rectangle{
width: parent.width - parent.height *2 - parent.spacing*2
height: parent.height
color: Colors.primaryContainer
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text{
text: "[" + modelData.correct + "]"
width: parent.width
height: parent.height
anchors.centerIn: parent
anchors.horizontalCenterOffset: 10
font.pixelSize: parent.height * 0.8
color: modelData.correctness ? "green" : "red"
}
}
}
}
}
}
Rectangle{
width: parent.width * 0.75
height: parent.height
color: "transparent"
Column{
width: parent.width
height: parent.height
spacing: 10
Row {
spacing: 10
width: parent.width
height: parent.height * 4 / 10 - parent.spacing * 2 / 3
Rectangle {
color: "transparent"
width: (parent.width - (learnerLastResultKanaBG.width + learnerLastResultTextBG.width))/2
height: parent.height
}
Rectangle {
id: learnerLastResultKanaBG
color: results[results.length-1].correctness ? "green" : "red"
width: parent.height - parent.spacing
height: parent.height
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: learnerLastResultKana
text: results[results.length-1].kana
width: parent.width
height: parent.height
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height * 0.1
font.pixelSize: parent.height * 0.8
color: Colors.primary
}
}
Rectangle {
id: learnerLastResultTextBG
color: results[results.length-1].correctness ? "green" : "red"
width: learnerLastResultText.width + 2 * parent.spacing
height: parent.height
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: learnerLastResultText
text: "[" + results[results.length-1].correct + "]"
anchors.centerIn: parent
font.pixelSize: learnerLastResultKana.font.pixelSize/2
color: Colors.primary
}
}
}
Rectangle {
id: learnerResultTextBG
color: Colors.primaryContainer
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width
height: parent.height * 3 / 10 - parent.spacing * 2 / 3
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: learnerResultText
text: result*100 + "% Richtig"
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: Colors.primary
}
}
Row {
width: parent.width
height: parent.height * 3 / 10 - parent.spacing * 2 / 3
spacing: 10
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
width: parent.width / 2 - parent.spacing
height: parent.height
onClicked: {
console.log("Choosing White Window")
mode === "KanaVoicing" ? learnerResult.parent.switch_KanaVoicing() : learnerResult.parent.switch_VoicingKana()
}
Rectangle {
anchors.fill: parent
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
radius: 10
border.width: 2
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
Text {
text: "Again"
anchors.centerIn: parent
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
}
}
}
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
width: parent.width / 2 - parent.spacing
height: parent.height
onClicked: {
console.log("Choosing Black Window")
learnerResult.parent.switchModeChooser()
}
Rectangle {
anchors.fill: parent
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
radius: 10
border.width: 2
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
Text {
text: "Menu"
anchors.centerIn: parent
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
}
}
}
}
}
}
}
}
}
+80
View File
@@ -0,0 +1,80 @@
import QtQuick
QtObject {
property var kana: [
{ hira: "あ", voice: ["a"], known: true },
{ hira: "い", voice: ["i"], known: true },
{ hira: 'う', voice: ["u"], known: true },
{ hira: 'え', voice: ["e"], known: true },
{ hira: 'お', voice: ["o"], known: true },
{ hira: 'か', voice: ["ka"], known: true },
{ hira: 'が', voice: ["ga"], known: true },
{ hira: 'き', voice: ["ki"], known: true },
{ hira: 'ぎ', voice: ["gi"], known: true },
{ hira: 'く', voice: ["ku"], known: true },
{ hira: 'ぐ', voice: ["gu"], known: true },
{ hira: 'け', voice: ["ke"], known: true },
{ hira: 'げ', voice: ["ge"], known: true },
{ hira: 'こ', voice: ["ko"], known: true },
{ hira: 'ご', voice: ["go"], known: true },
{ hira: 'さ', voice: ["sa"], known: true },
{ hira: 'ざ', voice: ["za"], known: true },
{ hira: 'し', voice: ["si", "shi"], known: true },
{ hira: 'じ', voice: ["zi", "ji"], known: true },
{ hira: 'す', voice: ["su"], known: true },
{ hira: 'ず', voice: ["zu"], known: true },
{ hira: 'せ', voice: ["se"], known: true },
{ hira: 'ぜ', voice: ["ze"], known: true },
{ hira: 'そ', voice: ["so"], known: true },
{ hira: 'ぞ', voice: ["zo"], known: true },
{ hira: 'た', voice: ["ta"], known: true },
{ hira: 'だ', voice: ["da"], known: true },
{ hira: 'ち', voice: ["ti", "shi"], known: true },
{ hira: 'ぢ', voice: ["di", "ji"], known: true },
{ hira: 'つ', voice: ["tu", "tsu"], known: true },
{ hira: 'づ', voice: ["du", "zu"], known: true },
{ hira: 'て', voice: ["te"], known: true },
{ hira: 'で', voice: ["de"], known: true },
{ hira: 'と', voice: ["to"], known: true },
{ hira: 'ど', voice: ["do"], known: true },
{ hira: 'な', voice: ["na"], known: true },
{ hira: 'に', voice: ["ni"], known: true },
{ hira: 'ぬ', voice: ["nu"], known: false },
{ hira: 'ね', voice: ["ne"], known: false },
{ hira: 'の', voice: ["no"], known: false },
{ hira: 'は', voice: ["ha"], known: false },
{ hira: 'ば', voice: ["ba"], known: false },
{ hira: 'ぱ', voice: ["pa"], known: false },
{ hira: 'ひ', voice: ["hi"], known: false },
{ hira: 'び', voice: ["bi"], known: false },
{ hira: 'ぴ', voice: ["pi"], known: false },
{ hira: 'ふ', voice: ["hu"], known: false },
{ hira: 'ぶ', voice: ["bu"], known: false },
{ hira: 'ぷ', voice: ["pu"], known: false },
{ hira: 'へ', voice: ["he"], known: false },
{ hira: 'べ', voice: ["be"], known: false },
{ hira: 'ぺ', voice: ["pe"], known: false },
{ hira: 'ほ', voice: ["ho"], known: false },
{ hira: 'ぼ', voice: ["bo"], known: false },
{ hira: 'ぽ', voice: ["po"], known: false },
{ hira: 'ま', voice: ["ma"], known: false },
{ hira: 'み', voice: ["mi"], known: false },
{ hira: 'む', voice: ["mu"], known: false },
{ hira: 'め', voice: ["me"], known: false },
{ hira: 'も', voice: ["mo"], known: false },
{ hira: 'や', voice: ["ya"], known: false },
{ hira: 'ゆ', voice: ["yu"], known: false },
{ hira: 'よ', voice: ["yo"], known: false },
{ hira: 'ら', voice: ["ra"], known: false },
{ hira: 'り', voice: ["ri"], known: false },
{ hira: 'る', voice: ["ru"], known: false },
{ hira: 'れ', voice: ["re"], known: false },
{ hira: 'ろ', voice: ["ro"], known: false },
{ hira: 'わ', voice: ["wa"], known: false },
{ hira: 'ゐ', voice: ["wi"], known: false },
{ hira: 'ゑ', voice: ["we"], known: false },
{ hira: 'を', voice: ["wo"], known: false },
{ hira: 'ん', voice: ["n"], known: true },
{ hira: 'ゔ', voice: ["vu"], known: false }
]
}
+184
View File
@@ -0,0 +1,184 @@
import QtQuick
import QtQuick.Controls
import "../../../colors"
Item { // Kana_Try
id: kanaVoicing
width: parent.width
height: parent.height
anchors.margins: 15
visible: false
// Component.onCompleted: {
// randomKnownKana()
// }
function start() {
kanaVoicingAnswer.focus = true
roundNum = 0
randomKnownKana()
}
KanaData {
id: kanaData
}
property var currentKana: []
property int roundNum: 0
property int correct: 0
property int maxRoundNum: 10
property var pastKana: []
property var pastCorrectness: []
function endRounds() {
kanaVoicingAnswer.focus = false
jLearnerScope.focus = true
console.log("Switching back")
parent.switchResult(correct/maxRoundNum, "KanaVoicing", pastCorrectness)
pastKana = []
pastCorrectness = []
correct = 0
kanaVoicingAnswer.placeholderText = "Type Voicing..."
kanaVoicingAnswerBackground.color = kanaVoicingAnswer.activeFocus ? Colors.tertiary_fg : Colors.primary_fg
}
function randomKana() {
if (roundNum >= maxRoundNum) {
endRounds()
}
var list = kanaData.kana
currentKana = list[Math.floor(Math.random() * list.length)]
if (knownList.length > maxRoundNum) {
while (pastKana.includes(currentKana)) {
currentKana = list[Math.floor(Math.random() * list.length)]
}
}
pastKana.push(currentKana)
console.log("Round Num: " + roundNum + " - Choosing Kana: " + currentKana.hira)
roundNum = roundNum + 1
}
function randomKnownKana() {
if (roundNum >= maxRoundNum) {
endRounds()
}
var knownList = kanaData.kana.filter(k => k.known)
currentKana = knownList[Math.floor(Math.random() * knownList.length)]
if (knownList.length > maxRoundNum) {
while (pastKana.includes(currentKana)) {
currentKana = knownList[Math.floor(Math.random() * knownList.length)]
}
}
pastKana.push(currentKana)
console.log("Round Num: " + roundNum + " - Choosing Kana: " + currentKana.hira)
roundNum = roundNum + 1
}
Column {
anchors.fill: parent
spacing: 10
Rectangle {
id: kanaVoicingTextRectangale
color: Colors.primaryContainer
width: kanaVoicingText.width + 20
height: parent.height * 0.1
anchors.horizontalCenter: parent.horizontalCenter
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: kanaVoicingText
text: "Im Spiel - Runde: " + kanaVoicing.roundNum
anchors.centerIn: parent
font.pixelSize: parent.height * 0.6
color: Colors.primary
}
}
Rectangle {
id: kanaVoicingKanaRectangle
color: Colors.primaryContainer
height: (parent.height - kanaVoicingTextRectangale.height)*0.75
width: height
anchors.horizontalCenter: parent.horizontalCenter
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: kanaVoicingKana
text: typeof kanaVoicing.currentKana.hira === "undefined" ? "" : kanaVoicing.currentKana.hira
anchors.centerIn: parent
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: Colors.primary
}
}
TextField {
id: kanaVoicingAnswer
placeholderText: "Type Voicing..."
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width/2
height: (parent.height - kanaVoicingTextRectangale.height)*0.25 - parent.spacing*2
font.pixelSize: height * 0.8
color: Colors.secondary_fg
placeholderTextColor: Colors.secondary
background: Rectangle {
id: kanaVoicingAnswerBackground
radius: 10
color: kanaVoicingAnswer.activeFocus ? Colors.secondaryContainer : Colors.secondaryContainer_fg
border.color: kanaVoicingAnswer.activeFocus ? Colors.secondaryContainer_fg : Colors.secondaryContainer
border.width: 1
}
Keys.onReleased: function(event) {
if (event.key === Qt.Key_Escape) {
console.log("Escape pressed inside TextField")
console.log("Pressed Escape, closing JLearner")
jLearnerLoader.active = false
}
}
onAccepted: function(event) {
console.log("Enter pressed inside TextField")
if (kanaVoicing.currentKana.voice.includes(kanaVoicingAnswer.text.toLowerCase())){
console.log("Correct")
kanaVoicingAnswer.text = ""
kanaVoicingAnswer.placeholderText = "[" + kanaVoicing.currentKana.voice + "]"
kanaVoicingAnswerBackground.color = "green"
kanaVoicing.correct = kanaVoicing.correct + 1
pastCorrectness.push({
kana: kanaVoicing.currentKana.hira,
correctness: true,
answer: kanaVoicingAnswer.text.toLowerCase(),
correct: kanaVoicing.currentKana.voice.join("|")
})
} else {
console.log("Wrong: [" + currentKana.voice + "]")
kanaVoicingAnswer.text = ""
kanaVoicingAnswerBackground.color = "red"
kanaVoicingAnswer.placeholderText = "[" + kanaVoicing.currentKana.voice + "]"
pastCorrectness.push({
kana: kanaVoicing.currentKana.hira,
correctness: false,
answer: kanaVoicingAnswer.text.toLowerCase(),
correct: kanaVoicing.currentKana.voice.join("|")
})
}
randomKnownKana()
}
}
}
}
+98
View File
@@ -0,0 +1,98 @@
import QtQuick
import QtQuick.Controls
import "../../../colors"
Item { // Chooser
id: languageChooser
width: parent.width
height: parent.height
anchors.margins: 15
visible: false
Column {
anchors.fill: parent
spacing: 10
Rectangle {
id: modeChooserUpperText
color: Colors.primaryContainer
width: mainText.width + 20
height: parent.height * 0.1
anchors.horizontalCenter: parent.horizontalCenter
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: mainText
text: "Wähle ein Modus:"
anchors.centerIn: parent
font.pixelSize: parent.height * 0.6
color: Colors.primary
}
}
Row {
width: parent.width
height: parent.height - modeChooserUpperText.height - parent.spacing
spacing: 10
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
width: (parent.width - parent.spacing) / 2
height: parent.height
onClicked: {
console.log("Choosing White Window")
languageChooser.parent.switch_KanaVoicing()
}
Rectangle {
anchors.fill: parent
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
radius: 10
border.width: 2
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
Text {
text: "Kana to Voicing"
anchors.centerIn: parent
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
}
}
}
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
width: (parent.width - parent.spacing) / 2
height: parent.height
onClicked: {
console.log("Choosing Black Window")
languageChooser.parent.switch_VoicingKana()
}
Rectangle {
anchors.fill: parent
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
radius: 10
border.width: 2
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
Text {
text: "Voicing to Kana"
anchors.centerIn: parent
font.pixelSize: Math.min(parent.width / text.length * 1.2, parent.height * 0.8)
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
}
}
}
}
}
}
+234
View File
@@ -0,0 +1,234 @@
import QtQuick
import QtQuick.Controls
import "../../../colors"
Item { // Kana_Try
id: voicingKana
width: parent.width
height: parent.height
anchors.margins: 15
visible: false
// Component.onCompleted: {
// randomKnownKana()
// }
function start() {
roundNum = 0
new_round()
}
KanaData {
id: kanaData
}
property int roundNum: 0
property int correct: 0
property int maxRoundNum: 10
property var pastKana: []
property var pastCorrectness: []
property var kanaArray: createResults(6)
property var targetKana: kanaArray[Math.floor(Math.random() * kanaArray.length)]
function createResults(n) {
var arr = []
for (var i = 0; i < n; i++) {
arr.push({ hira: "あ", voice: ["a"], known: true })
}
return arr
}
function new_round() {
roundNum = roundNum + 1
kanaArray = gemerateKanaArray()
targetKana = kanaArray[Math.floor(Math.random() * kanaArray.length)]
console.log("Round Num: " + roundNum + " - Choosing Kana: " + targetKana.hira)
pastKana.push(targetKana)
}
function gemerateKanaArray() {
var arr = []
for (var i = 0; i < 6; i++) {
arr.push(randomKnownKana(arr))
}
return arr
}
function randomKana(arr) {
if (roundNum > maxRoundNum) {
endRounds()
}
var list = kanaData.kana
var newKana = list[Math.floor(Math.random() * list.length)]
var arrExists = Array.isArray(arr)
if (knownList.length > maxRoundNum + 6) {
if (arrExists) {
while (pastKana.includes(newKana) || arr.includes(newKana)) {
newKana = list[Math.floor(Math.random() * list.length)]
}
} else{
while (pastKana.includes(newKana)){
newKana = list[Math.floor(Math.random() * list.length)]
}
}
} else {
if (arrExists) {
while (arr.includes(newKana)) {
newKana = list[Math.floor(Math.random() * list.length)]
}
}
}
kanaArray.push(newKana)
return newKana
}
function randomKnownKana(arr) {
if (roundNum > maxRoundNum) {
endRounds()
}
var knownList = kanaData.kana.filter(k => k.known)
var newKana = knownList[Math.floor(Math.random() * knownList.length)]
var arrExists = Array.isArray(arr)
if (knownList.length > maxRoundNum + 6) {
if (arrExists) {
while (pastKana.includes(newKana) || arr.includes(newKana)) {
newKana = knownList[Math.floor(Math.random() * knownList.length)]
}
} else{
while (pastKana.includes(newKana)){
newKana = knownList[Math.floor(Math.random() * knownList.length)]
}
}
} else {
if (arrExists) {
while (arr.includes(newKana)) {
newKana = knownList[Math.floor(Math.random() * knownList.length)]
}
}
}
return newKana
}
function endRounds() {
console.log("Switching back")
roundNum = 0
parent.switchResult(correct/maxRoundNum, "VoicingKana", pastCorrectness)
}
Column {
anchors.fill: parent
spacing: 10
Rectangle {
id: voicingKanaTextRectangale
color: Colors.primaryContainer
width: mainText.width + 20
height: parent.height * 0.1
anchors.horizontalCenter: parent.horizontalCenter
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: mainText
text: "Runde: " + roundNum + " - [" + targetKana.voice.join("|") + "]:"
anchors.centerIn: parent
font.pixelSize: parent.height * 0.6
color: Colors.primary
}
}
Rectangle{
width: parent.width
height: parent.height - voicingKanaTextRectangale.height - voicingKanaResultRectangale.height - parent.spacing * 2
color: "transparent"
Grid {
id: grid
anchors.fill: parent
columns: 3
spacing: 10
property int rows: 2
// calculate cell width and height based on parent size, spacing, and number of columns/rows
property real cellWidth: (width - (columns - 1) * spacing) / columns
property real cellHeight: (height - (rows - 1) * spacing) / rows
Repeater {
model: kanaArray
MouseArea {
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
width: grid.cellWidth
height: grid.cellHeight
onClicked: {
console.log("Choose a Kana: " + modelData.hira)
if (modelData.hira === targetKana.hira) {
console.log("Correct")
correct = correct + 1
voicingKanaResultRectangale.color = "green"
} else {
console.log("Wrong: [" + targetKana.hira + "]")
voicingKanaResultRectangale.color = "red"
}
pastCorrectness.push({
kana: targetKana.hira,
correctness: modelData.hira === targetKana.hira,
answer: modelData.hira,
correct: targetKana.voice.join("|")
})
voicingKanaResult.text = "Letzte Runde: " + targetKana.hira + ": " + "[" + targetKana.voice.join("|") + "]"
new_round()
}
Rectangle {
width: parent.width
height: parent.height
color: parent.containsMouse ? Colors.secondaryContainer_fg : Colors.secondaryContainer
radius: 10
border.width: 2
border.color: parent.containsMouse ? Colors.secondaryContainer : Colors.secondaryContainer_fg
Text{
text: modelData.hira
anchors.centerIn: parent
anchors.verticalCenterOffset: -parent.height * 0.1
font.pixelSize: parent.height * 0.8
color: parent.parent.containsMouse ? Colors.secondary_fg : Colors.secondary
}
}
}
}
}
}
Rectangle {
id: voicingKanaResultRectangale
color: Colors.primaryContainer
width: voicingKanaResult.width + 20
height: parent.height * 0.1
anchors.horizontalCenter: parent.horizontalCenter
radius: 10
border.width: 2
border.color: Colors.primaryContainer_fg
Text {
id: voicingKanaResult
text: "Wähle das richtige Zeichen"
anchors.centerIn: parent
font.pixelSize: parent.height * 0.6
color: Colors.primary
}
}
}
}
+29 -1
View File
@@ -14,6 +14,23 @@ PanelWindow {
visible: true visible: true
focusable: true focusable: true
FocusScope {
id: pickerScope
focus: true
Keys.onReleased: function(event) {
if (event.key === Qt.Key_Escape) {
console.log("Pressed Escape, closing Wallpaper Picker")
wallpaperLoader.active = false
}
}
Rectangle {
anchors.fill: parent
color: "lightblue"
}
}
Rectangle { Rectangle {
anchors.fill: parent anchors.fill: parent
radius: 10 radius: 10
@@ -42,11 +59,22 @@ PanelWindow {
background: Rectangle { background: Rectangle {
radius: 12 radius: 12
color: "#2b2b2b" color: filterText.activeFocus ? Colors.tertiary_fg : Colors.primary_fg
border.color: filterText.activeFocus ? Colors.tertiary : Colors.primary border.color: filterText.activeFocus ? Colors.tertiary : Colors.primary
border.width: 1 border.width: 1
} }
Keys.onReleased: {
if (event.key === Qt.Key_Escape) {
console.log("Escape pressed inside TextField")
console.log("Pressed Escape, closing Wallpaper Picker")
// wallpaperLoader.active = false
filterText.text = ""
filterText.focus = false
event.accepted = true
pickerScope.focus = true
}
}
} }
+1 -1
View File
@@ -28,8 +28,8 @@ Item { // container, invisible
onPressed: { onPressed: {
// generateThumbs.startDetached() // generateThumbs.startDetached()
generateThumbs.running = true generateThumbs.running = true
wallpaperLoader.active = !wallpaperLoader.active
console.log("Pressed Meta+Shift+W (Hyprland global shortcut)") console.log("Pressed Meta+Shift+W (Hyprland global shortcut)")
wallpaperLoader.active = !wallpaperLoader.active
} }
} }
+51 -2
View File
@@ -10,7 +10,9 @@ if [ ! -d $THUMB_DIR ]; then
mkdir -p $THUMB_DIR; mkdir -p $THUMB_DIR;
fi fi
shopt -s nullglob
files=( "$THUMB_DIR"/*.jpg "$THUMB_DIR"/*.jpeg "$THUMB_DIR"/*.png )
# printf ' %s\n' "${files[@]}"
# Make sure the thumb directory exists # Make sure the thumb directory exists
mkdir -p "$THUMB_DIR" mkdir -p "$THUMB_DIR"
@@ -19,6 +21,28 @@ MAX_DIM=300
num=0 num=0
remove_from_array() {
local -n arr=$1
local elem=$2
local result=()
# echo "=== Removing element: '$elem' ==="
# echo "Original array has ${#arr[@]} elements"
for item in "${arr[@]}"; do
if [[ "$item" != "$elem" ]]; then
result+=( "$item" )
# echo "Keeping: '$item'"
# else
# echo "Removing: '$item'"
fi
done
arr=( "${result[@]}" )
# echo "New array has ${#arr[@]} elements"
# echo "-----------------------------------"
}
# Process each image # Process each image
for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
# Skip if no files match # Skip if no files match
@@ -26,19 +50,44 @@ for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
# Get just the filename # Get just the filename
fname=$(basename "$img") fname=$(basename "$img")
name="${fname%.*}"
ext="${fname##*.}"
# no magick file:
temp_image_name="$THUMB_DIR/${name}_tmp.${ext}"
# Output path # Output path
thumb="$THUMB_DIR/$fname" thumb="$THUMB_DIR/$fname"
# If thumbnail already exists and is newer than the original, skip # If thumbnail already exists and is newer than the original, skip
if [ -f "$thumb" ] && [ "$thumb" -nt "$img" ]; then if [ -f "$thumb" ] && [ "$thumb" -nt "$img" ]; then
remove_from_array files "$thumb"
continue continue
fi fi
num+=1 num+=1
# Generate thumbnail using ImageMagick # Generate thumbnail using ImageMagick
magick "$img" -resize "${MAX_DIM}x${MAX_DIM}" "$thumb" if [ -x "/usr/bin/magick" ]; then
magick "$img" -resize "${MAX_DIM}x${MAX_DIM}" "$thumb"
rm $temp_image_name
else
if [ -f "$temp_image_name" ] && [ "$temp_image_name" -nt "$img" ]; then
continue
fi
cp $img $temp_image_name
fi
remove_from_array files "$thumb"
done
# echo ""
# printf ' %s\n' "${files[@]}"
for img in $files; do
# echo "$img"
rm $img
done done
echo "$num Thumbnails generated in $THUMB_DIR" echo "$num Thumbnails generated in $THUMB_DIR"
+15 -2
View File
@@ -42,10 +42,15 @@ elif [ -x "/usr/bin/hyprpaper" ]; then
sleep 0.2 sleep 0.2
echo "daemon started" echo "daemon started"
fi fi
sed -i "3s|path = .*|path = ${image}|" ~/.config/hypr/hyprpaper.conf
hyprctl hyprpaper wallpaper ", $image" hyprctl hyprpaper wallpaper ", $image"
fi fi
matugen image $image --mode dark if [ -x "/usr/bin/matugen" ]; then
matugen image $image --mode dark
else
cp $HOME/.config/quickshell/colors/example_colors.qml $HOME/.config/quickshell/colors/Colors.qml
fi
### Blured Wallpaper for wlogout ### Blured Wallpaper for wlogout
blur="50x30" blur="50x30"
@@ -61,6 +66,14 @@ echo "$image" > $current_wallpaper
blurred_wp="$static_wallpaper_dir/blurred.jpg" blurred_wp="$static_wallpaper_dir/blurred.jpg"
magick $image -blur $blur $blurred_wp if [ -x "/usr/bin/sddm" ]; then
magick $image -blur $blur $blurred_wp
else
cp $image $blurred_wp
fi
if [ -x "/usr/bin/sddm" ] && [ -d "/usr/share/backgrounds" ]; then
sddm_background="/usr/share/backgrounds/background.jpg"
cp "$image" "$sddm_background"
fi
echo "test" echo "test"
+4
View File
@@ -5,6 +5,7 @@ import Quickshell.Hyprland
import QtQuick //for Text import QtQuick //for Text
import "./modules/bar" import "./modules/bar"
import "./modules/wp" import "./modules/wp"
import "./modules/japanese"
ShellRoot { ShellRoot {
id: root id: root
@@ -15,4 +16,7 @@ ShellRoot {
//Wallpaper picker //Wallpaper picker
WallpaperPickerLoader {} WallpaperPickerLoader {}
//Japanese Lerner
JLearnerLoader {}
} }