Merge branch 'main' of github.com:Teoro01/hyprland-quickshell-gui
This commit is contained in:
@@ -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 |
@@ -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"
|
||||
}
|
||||
@@ -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 |
@@ -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 |
@@ -77,8 +77,5 @@ PanelWindow {
|
||||
PowerButton {}
|
||||
|
||||
}
|
||||
|
||||
// TODO: Somewhere add Tray
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+127
@@ -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"
|
||||
@@ -4,16 +4,47 @@ import Quickshell.Hyprland
|
||||
import "../../../colors"
|
||||
|
||||
Rectangle { //workspace
|
||||
width: timeDisplay.width + 16
|
||||
width: timeDisplay.width + 8 + dateDisplay.width + 16
|
||||
height: timeDisplay.height + 16
|
||||
radius: 4
|
||||
color: Colors.primaryContainer
|
||||
border.color: Colors.primaryContainer_fg
|
||||
border.width: 2
|
||||
MouseArea {
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
anchors.fill: parent
|
||||
acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
|
||||
|
||||
Row {
|
||||
id: row
|
||||
spacing: 8
|
||||
anchors.centerIn: parent
|
||||
|
||||
Text {
|
||||
id: dateDisplay
|
||||
property string currentDate: ""
|
||||
|
||||
text: currentDate
|
||||
color: Colors.primary
|
||||
font.pixelSize: 18
|
||||
font.family: "Inter, sans-serif"
|
||||
|
||||
Timer {
|
||||
id: dateTimer
|
||||
interval: 60000
|
||||
running: false
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: {
|
||||
var now = new Date()
|
||||
dateDisplay.currentDate = Qt.formatDate(now, "dd.MM.yyyy")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: timeDisplay
|
||||
|
||||
property string currentTime: ""
|
||||
|
||||
text: currentTime
|
||||
@@ -21,9 +52,11 @@ Rectangle { //workspace
|
||||
font.pixelSize: 18
|
||||
font.family: "Inter, sans-serif"
|
||||
|
||||
anchors.centerIn: parent
|
||||
Component.onCompleted: {
|
||||
var now = new Date()
|
||||
currentTime = Qt.formatTime(now, "hh:mm:ss")
|
||||
}
|
||||
|
||||
//Update time every second
|
||||
Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
@@ -35,3 +68,9 @@ Rectangle { //workspace
|
||||
}
|
||||
}
|
||||
}
|
||||
onClicked: {
|
||||
dateDisplay.currentDate = "";
|
||||
dateTimer.running = !dateTimer.running;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import "../../../colors"
|
||||
|
||||
MouseArea {
|
||||
id: networkButton
|
||||
width: output.width + 16
|
||||
width: bar.height
|
||||
height: bar.height
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
@@ -19,7 +19,9 @@ MouseArea {
|
||||
anchors.fill: parent
|
||||
color: networkButton.containsMouse ? Colors.tertiary : Qt.alpha(Colors.tertiaryContainer, 0.25)
|
||||
radius: 6
|
||||
width: output.width + 16
|
||||
border.width: 2
|
||||
border.color: networkButton.containsMouse ? Colors.tertiaryContainer_fg
|
||||
: Colors.secondaryContainer_fg
|
||||
|
||||
Text {
|
||||
id: output
|
||||
@@ -37,7 +39,7 @@ MouseArea {
|
||||
stdout: StdioCollector {
|
||||
onStreamFinished: {
|
||||
output.text = this.text.trim()
|
||||
console.log("networkScript output:", this.text.trim())
|
||||
// console.log("networkScript output:", this.text.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,20 @@ import "../../../colors"
|
||||
|
||||
Row { // Workspace Row
|
||||
id: trayRow
|
||||
spacing: 8
|
||||
spacing: 4
|
||||
|
||||
Repeater {
|
||||
model: SystemTray.items
|
||||
|
||||
Column{
|
||||
Rectangle{
|
||||
width: iconImage.width + 8
|
||||
height: (bar.height-trayItem.height)/2
|
||||
color: "transparent"
|
||||
}
|
||||
Rectangle { //trayItem
|
||||
id: trayItem
|
||||
width: bar.height
|
||||
height: bar.height
|
||||
width: iconImage.width + 8
|
||||
height: iconImage.height + 8
|
||||
radius: 4
|
||||
color: "transparent"
|
||||
border.width: 2
|
||||
@@ -76,17 +81,72 @@ Row { // Workspace Row
|
||||
|
||||
Image { // Image
|
||||
id: iconImage
|
||||
source: modelData.icon
|
||||
// source: modelData.icon; Does not work for some tray icons
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: bar.height*0.75
|
||||
height: bar.height*0.75
|
||||
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;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import Quickshell.Io
|
||||
import "../../../colors"
|
||||
|
||||
Row { // Workspace Row
|
||||
@@ -9,12 +10,28 @@ Row { // Workspace Row
|
||||
required property HyprlandMonitor thisMonitor
|
||||
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
|
||||
model: Hyprland.workspaces
|
||||
|
||||
Rectangle { //workspace
|
||||
visible: modelData.monitor === thisMonitor
|
||||
width: workspaceNum.width + 26
|
||||
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
|
||||
@@ -49,8 +66,28 @@ Row { // Workspace Row
|
||||
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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 }
|
||||
]
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,23 @@ PanelWindow {
|
||||
visible: 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 {
|
||||
anchors.fill: parent
|
||||
radius: 10
|
||||
@@ -42,11 +59,22 @@ PanelWindow {
|
||||
background: Rectangle {
|
||||
|
||||
radius: 12
|
||||
color: "#2b2b2b"
|
||||
color: filterText.activeFocus ? Colors.tertiary_fg : Colors.primary_fg
|
||||
border.color: filterText.activeFocus ? Colors.tertiary : Colors.primary
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ Item { // container, invisible
|
||||
onPressed: {
|
||||
// generateThumbs.startDetached()
|
||||
generateThumbs.running = true
|
||||
wallpaperLoader.active = !wallpaperLoader.active
|
||||
console.log("Pressed Meta+Shift+W (Hyprland global shortcut)")
|
||||
wallpaperLoader.active = !wallpaperLoader.active
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ if [ ! -d $THUMB_DIR ]; then
|
||||
mkdir -p $THUMB_DIR;
|
||||
fi
|
||||
|
||||
|
||||
shopt -s nullglob
|
||||
files=( "$THUMB_DIR"/*.jpg "$THUMB_DIR"/*.jpeg "$THUMB_DIR"/*.png )
|
||||
# printf ' %s\n' "${files[@]}"
|
||||
# Make sure the thumb directory exists
|
||||
mkdir -p "$THUMB_DIR"
|
||||
|
||||
@@ -19,6 +21,28 @@ MAX_DIM=300
|
||||
|
||||
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
|
||||
for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
|
||||
# Skip if no files match
|
||||
@@ -26,19 +50,44 @@ for img in "$WALLPAPER_DIR"/*.{jpg,jpeg,png}; do
|
||||
|
||||
# Get just the filename
|
||||
fname=$(basename "$img")
|
||||
name="${fname%.*}"
|
||||
ext="${fname##*.}"
|
||||
|
||||
# no magick file:
|
||||
temp_image_name="$THUMB_DIR/${name}_tmp.${ext}"
|
||||
|
||||
# Output path
|
||||
thumb="$THUMB_DIR/$fname"
|
||||
|
||||
|
||||
# If thumbnail already exists and is newer than the original, skip
|
||||
if [ -f "$thumb" ] && [ "$thumb" -nt "$img" ]; then
|
||||
remove_from_array files "$thumb"
|
||||
continue
|
||||
fi
|
||||
|
||||
num+=1
|
||||
|
||||
# Generate thumbnail using ImageMagick
|
||||
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
|
||||
|
||||
echo "$num Thumbnails generated in $THUMB_DIR"
|
||||
@@ -42,10 +42,15 @@ elif [ -x "/usr/bin/hyprpaper" ]; then
|
||||
sleep 0.2
|
||||
echo "daemon started"
|
||||
fi
|
||||
sed -i "3s|path = .*|path = ${image}|" ~/.config/hypr/hyprpaper.conf
|
||||
hyprctl hyprpaper wallpaper ", $image"
|
||||
fi
|
||||
|
||||
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
|
||||
blur="50x30"
|
||||
@@ -61,6 +66,14 @@ echo "$image" > $current_wallpaper
|
||||
|
||||
blurred_wp="$static_wallpaper_dir/blurred.jpg"
|
||||
|
||||
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"
|
||||
|
||||
Reference in New Issue
Block a user