mirror of https://github.com/wayvr-org/wayvr.git
dash-frontend: Application pinning (#565)
* dash-frontend: App pinning (wip) * App pinning and unpinning, preserve settings (done)
This commit is contained in:
parent
cfeef62215
commit
80237d36cd
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="currentColor" d="M15 14v-2h8v2zm-4 7l-3.175-2.85q-1.8-1.625-3.088-2.9t-2.125-2.4t-1.225-2.175T1 8.475q0-2.35 1.575-3.912T6.5 3q1.3 0 2.475.538T11 5.075q.85-1 2.025-1.537T15.5 3q2.125 0 3.563 1.288T20.85 7.3q-.45-.175-.9-.262t-.875-.088q-2.525 0-4.3 1.763T13 13q0 1.3.525 2.463T15 17.45q-.475.425-1.237 1.088T12.45 19.7z"/></svg>
|
||||||
|
After Width: | Height: | Size: 538 B |
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><!-- Icon from Material Symbols by Google - https://github.com/google/material-design-icons/blob/master/LICENSE --><path fill="currentColor" d="M18.288 16.713Q18 16.425 18 16v-2h-2q-.425 0-.712-.288T15 13t.288-.712T16 12h2v-2q0-.425.288-.712T19 9t.713.288T20 10v2h2q.425 0 .713.288T23 13t-.288.713T22 14h-2v2q0 .425-.288.713T19 17t-.712-.288M1.75 23.626v-3.8zM10.3 20.2q-.35-.125-.625-.4q-1.1-1-2.6-2.275t-2.85-2.737t-2.287-3.063T1 8.475q0-2.35 1.575-3.912T6.5 3q1.3 0 2.475.538T11 5.075q.85-1 2.025-1.537T15.5 3q1.575 0 2.788.738T20.2 5.6q.275.475-.087.963T19.075 7h-.4q-2.125 0-3.9 1.713T13 13q0 1.05.338 1.988T14.4 16.7q.275.3.238.7t-.338.675l-1.975 1.75q-.275.275-.625.388t-.7.112t-.7-.125"/></svg>
|
||||||
|
After Width: | Height: | Size: 785 B |
|
|
@ -1,5 +1,5 @@
|
||||||
<layout>
|
<layout>
|
||||||
<template name="Separator">
|
<template name="Separator">
|
||||||
<rectangle width="100%" height="1" color="#FFFFFF77" />
|
<rectangle width="100%" min_height="2" color="#FFFFFF10" />
|
||||||
</template>
|
</template>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
<layout>
|
<layout>
|
||||||
<include src="../theme.xml" />
|
<include src="../theme.xml" />
|
||||||
|
<include src="../t_group_box.xml" />
|
||||||
|
|
||||||
|
<!-- tooltip, src, src_ext, name -->
|
||||||
<template name="AppEntry">
|
<template name="AppEntry">
|
||||||
<Button
|
<Button
|
||||||
id="button" width="116" max_width="140" min_height="100" flex_grow="1"
|
id="button" tooltip_side="bottom" tooltip="${tooltip}" width="116" max_width="140" min_height="100" flex_grow="1"
|
||||||
flex_direction="column" overflow="visible" align_items="center" justify_content="center" gap="4"
|
flex_direction="column" overflow="visible" align_items="center" justify_content="center" gap="4"
|
||||||
color="#3385FF10"
|
color="#3385FF10"
|
||||||
>
|
>
|
||||||
|
|
@ -39,6 +41,15 @@
|
||||||
</rectangle>
|
</rectangle>
|
||||||
</div>
|
</div>
|
||||||
-->
|
-->
|
||||||
|
<rectangle macro="group_box">
|
||||||
|
<div align_items="center" gap="8">
|
||||||
|
<sprite src_builtin="dashboard/heart_plus.svg" min_width="24" min_height="24"/>
|
||||||
|
<label translation="PINNED_APPS"/>
|
||||||
|
</div>
|
||||||
|
<div id="pinned_apps_parent" flex_direction="row" flex_wrap="wrap" gap="4">
|
||||||
|
<!-- filled-in at runtime -->
|
||||||
|
</div>
|
||||||
|
</rectangle>
|
||||||
<div
|
<div
|
||||||
id="app_list_parent"
|
id="app_list_parent"
|
||||||
flex_direction="row"
|
flex_direction="row"
|
||||||
|
|
@ -46,4 +57,4 @@
|
||||||
gap="4"
|
gap="4"
|
||||||
/>
|
/>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
<layout>
|
<layout>
|
||||||
<template name="Subtext">
|
<template name="Subtext">
|
||||||
<div flex_direction="row" gap="8">
|
<div flex_direction="row" gap="8" max_width="100%">
|
||||||
<label weight="bold" text="${title}" />
|
<label weight="bold" text="${title}" wrap="1" />
|
||||||
<label id="${label_id}" />
|
<label id="${label_id}" wrap="1" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -15,12 +15,10 @@
|
||||||
|
|
||||||
<elements>
|
<elements>
|
||||||
<div flex_direction="row" gap="16" flex_grow="1">
|
<div flex_direction="row" gap="16" flex_grow="1">
|
||||||
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
|
<rectangle macro="group_box" id="icon_parent" padding="16" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center"/>
|
||||||
|
<div flex_direction="column" gap="8" flex_grow="1">
|
||||||
</rectangle>
|
|
||||||
<div flex_direction="column" gap="8" flex_grow="1" max_width="720">
|
|
||||||
<label id="label_title" weight="bold" size="32" overflow="hidden" />
|
<label id="label_title" weight="bold" size="32" overflow="hidden" />
|
||||||
<Subtext label_id="label_exec" overflow="hidden" />
|
<Subtext label_id="label_exec"/>
|
||||||
<Separator />
|
<Separator />
|
||||||
<RadioGroup id="radio_compositor" flex_direction="row" gap="16">
|
<RadioGroup id="radio_compositor" flex_direction="row" gap="16">
|
||||||
<RadioBox translation="APP_LAUNCHER.MODE.NATIVE" value="Native" checked="1" />
|
<RadioBox translation="APP_LAUNCHER.MODE.NATIVE" value="Native" checked="1" />
|
||||||
|
|
@ -30,14 +28,14 @@
|
||||||
<label translation="APP_LAUNCHER.RES_TITLE" />
|
<label translation="APP_LAUNCHER.RES_TITLE" />
|
||||||
<RadioGroup id="radio_res" flex_direction="row" gap="16">
|
<RadioGroup id="radio_res" flex_direction="row" gap="16">
|
||||||
<RadioBox text="1440p" value="Res1440" />
|
<RadioBox text="1440p" value="Res1440" />
|
||||||
<RadioBox text="1080p" value="Res1080" checked="1" />
|
<RadioBox text="1080p" value="Res1080"/>
|
||||||
<RadioBox text="720p" value="Res720" />
|
<RadioBox text="720p" value="Res720" />
|
||||||
<RadioBox text="480p" value="Res480" />
|
<RadioBox text="480p" value="Res480" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
<Separator />
|
<Separator />
|
||||||
<label translation="APP_LAUNCHER.ASPECT_TITLE" />
|
<label translation="APP_LAUNCHER.ASPECT_TITLE" />
|
||||||
<RadioGroup id="radio_orientation" flex_direction="row" gap="16">
|
<RadioGroup id="radio_orientation" flex_direction="row" gap="16">
|
||||||
<RadioBox translation="APP_LAUNCHER.ASPECT.WIDE" value="Wide" tooltip_str="16:9" checked="1" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.WIDE" value="Wide" tooltip_str="16:9"/>
|
||||||
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_WIDE" value="SemiWide" tooltip_str="3:2" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_WIDE" value="SemiWide" tooltip_str="3:2" />
|
||||||
<RadioBox translation="APP_LAUNCHER.ASPECT.SQUARE" value="Square" tooltip_str="1:1" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SQUARE" value="Square" tooltip_str="1:1" />
|
||||||
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_TALL" value="SemiTall" tooltip_str="2:3" />
|
<RadioBox translation="APP_LAUNCHER.ASPECT.SEMI_TALL" value="SemiTall" tooltip_str="2:3" />
|
||||||
|
|
@ -51,9 +49,17 @@
|
||||||
<RadioBox translation="APP_LAUNCHER.POS.STATIC" value="Static" tooltip_str="APP_LAUNCHER.POS.STATIC_HELP" />
|
<RadioBox translation="APP_LAUNCHER.POS.STATIC" value="Static" tooltip_str="APP_LAUNCHER.POS.STATIC_HELP" />
|
||||||
</RadioGroup -->
|
</RadioGroup -->
|
||||||
<Separator />
|
<Separator />
|
||||||
<div flex_direction="row" justify_content="space_between" gap="16">
|
<CheckBox id="cb_autostart" translation="APP_LAUNCHER.AUTOSTART" align_self="baseline"/>
|
||||||
<CheckBox id="cb_autostart" translation="APP_LAUNCHER.AUTOSTART" />
|
<div flex_direction="row" align_items="end" justify_content="center" gap="8" margin_top="8">
|
||||||
<Button id="btn_launch" align_self="baseline" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12" min_height="40">
|
<Button id="btn_unpin" color="#997777" padding_left="8" padding_right="8" round="8" min_height="40">
|
||||||
|
<sprite src_builtin="dashboard/heart_minus.svg" width="32" height="32" />
|
||||||
|
<label translation="REMOVE_FROM_FAVOURITES" weight="bold" size="17" shadow="#00000099" />
|
||||||
|
</Button>
|
||||||
|
<Button id="btn_pin" color="#ce4477" padding_left="8" padding_right="8" round="8" min_height="40" tooltip="ADD_TO_FAVOURITES_HINT">
|
||||||
|
<sprite src_builtin="dashboard/heart_plus.svg" width="32" height="32" />
|
||||||
|
<label translation="ADD_TO_FAVOURITES" weight="bold" size="17" shadow="#00000099" />
|
||||||
|
</Button>
|
||||||
|
<Button id="btn_launch" color="#44ce22" padding_left="8" padding_right="8" round="8" min_width="200" min_height="40">
|
||||||
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
|
<sprite src_builtin="dashboard/play.svg" width="32" height="32" />
|
||||||
<label translation="APP_LAUNCHER.LAUNCH" weight="bold" size="17" shadow="#00000099" />
|
<label translation="APP_LAUNCHER.LAUNCH" weight="bold" size="17" shadow="#00000099" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -61,4 +67,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "Sie schalten die Sprache-zu-Text-Funktion aus.\nMöchten Sie die heruntergeladenen Whisper-Modelle entfernen, um Speicherplatz freizugeben?"
|
"REMOVE_UNUSED_MODELS": "Sie schalten die Sprache-zu-Text-Funktion aus.\nMöchten Sie die heruntergeladenen Whisper-Modelle entfernen, um Speicherplatz freizugeben?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "Sprach-zu-Text-Modell",
|
"WHISPER_MODEL": "Sprach-zu-Text-Modell",
|
||||||
"WHISPER_MODEL_HELP": "Zu verwendendes Whisper-Modell.\nGrößere Modelle liefern genauere\nErgebnisse, verbrauchen jedoch mehr VRAM."
|
"WHISPER_MODEL_HELP": "Zu verwendendes Whisper-Modell.\nGrößere Modelle liefern genauere\nErgebnisse, verbrauchen jedoch mehr VRAM.",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "Standardgröße des Overlays",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "Neue Overlays werden in dieser Skalierung erstellt."
|
||||||
},
|
},
|
||||||
"HELLO": "Hallo!",
|
"HELLO": "Hallo!",
|
||||||
"AUDIO": {
|
"AUDIO": {
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "Arbeits-Set"
|
"WORKING_SET": "Arbeits-Set"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "Herunterladen"
|
"DOWNLOAD": "Herunterladen",
|
||||||
|
"SAVED_TO_FAVOURITES": "Zu Favoriten hinzugefügt",
|
||||||
|
"ADD_TO_FAVOURITES": "Zu Favoriten hinzufügen",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "Voreinstellung speichern und an den App-Drawer anheften",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "Aus Favoriten entfernen",
|
||||||
|
"EMPTY": "Leer",
|
||||||
|
"PINNED_APPS": "Angepinnte Anwendungen"
|
||||||
}
|
}
|
||||||
|
|
@ -298,5 +298,11 @@
|
||||||
"TARGET_PATH": "Target path",
|
"TARGET_PATH": "Target path",
|
||||||
"TERMINATE_PROCESS": "Terminate process",
|
"TERMINATE_PROCESS": "Terminate process",
|
||||||
"VERSION": "Version",
|
"VERSION": "Version",
|
||||||
"WIDTH": "Width"
|
"WIDTH": "Width",
|
||||||
|
"SAVED_TO_FAVOURITES": "Saved to favourites",
|
||||||
|
"ADD_TO_FAVOURITES": "Add to favourites",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "Save preset and pin it to the app drawer",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "Remove from favourites",
|
||||||
|
"EMPTY": "Empty",
|
||||||
|
"PINNED_APPS": "Pinned applications"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -182,7 +182,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "Estás desactivando la función de voz a texto.\n¿Te gustaría eliminar los modelos de Whisper descargados para liberar espacio?"
|
"REMOVE_UNUSED_MODELS": "Estás desactivando la función de voz a texto.\n¿Te gustaría eliminar los modelos de Whisper descargados para liberar espacio?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "Modelo de voz a texto",
|
"WHISPER_MODEL": "Modelo de voz a texto",
|
||||||
"WHISPER_MODEL_HELP": "Modelo de Whisper a utilizar.\nLos modelos más grandes producen\nresultados más precisos, pero consumen más VRAM."
|
"WHISPER_MODEL_HELP": "Modelo de Whisper a utilizar.\nLos modelos más grandes producen\nresultados más precisos, pero consumen más VRAM.",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "Tamaño de la superposición predeterminado",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "Genera nuevas capas de superposición con esta escala."
|
||||||
},
|
},
|
||||||
"HELLO": "¡Hola!",
|
"HELLO": "¡Hola!",
|
||||||
"AUDIO": {
|
"AUDIO": {
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "Set de trabajo"
|
"WORKING_SET": "Set de trabajo"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "Descargar"
|
"DOWNLOAD": "Descargar",
|
||||||
|
"SAVED_TO_FAVOURITES": "Guardado en favoritos",
|
||||||
|
"ADD_TO_FAVOURITES": "Añadir a favoritos",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "Guarda el ajuste preestablecido y ánclalo al cajón de aplicaciones",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "Eliminar de favoritos",
|
||||||
|
"EMPTY": "Vacío",
|
||||||
|
"PINNED_APPS": "Aplicaciones ancladas"
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "Stai disattivando la funzione di sintesi vocale.\nDesideri rimuovere i modelli Whisper scaricati per liberare spazio?"
|
"REMOVE_UNUSED_MODELS": "Stai disattivando la funzione di sintesi vocale.\nDesideri rimuovere i modelli Whisper scaricati per liberare spazio?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "Modello di sintesi vocale (speech-to-text)",
|
"WHISPER_MODEL": "Modello di sintesi vocale (speech-to-text)",
|
||||||
"WHISPER_MODEL_HELP": "Modello Whisper da utilizzare.\nI modelli più grandi producono risultati\npiù accurati, ma consumano più VRAM."
|
"WHISPER_MODEL_HELP": "Modello Whisper da utilizzare.\nI modelli più grandi producono risultati\npiù accurati, ma consumano più VRAM.",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "Dimensioni overlay predefinite",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "Genera nuovi overlay con questa scala."
|
||||||
},
|
},
|
||||||
"APPLICATION_LAUNCHER": "Lanciatore applicazioni",
|
"APPLICATION_LAUNCHER": "Lanciatore applicazioni",
|
||||||
"APPLICATION_STARTED": "Applicazione avviata",
|
"APPLICATION_STARTED": "Applicazione avviata",
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "Set di lavoro"
|
"WORKING_SET": "Set di lavoro"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "Scarica"
|
"DOWNLOAD": "Scarica",
|
||||||
|
"SAVED_TO_FAVOURITES": "Salvato nei preferiti",
|
||||||
|
"ADD_TO_FAVOURITES": "Aggiungi ai preferiti",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "Salva il preset e fissalo al cassetto delle app",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "Rimuovi dai preferiti",
|
||||||
|
"EMPTY": "Vuoto",
|
||||||
|
"PINNED_APPS": "Applicazioni fissate"
|
||||||
}
|
}
|
||||||
|
|
@ -182,7 +182,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "文字起こし機能をオフにします。\n空き容量を増やすために、ダウンロード済みのモデルファイルを削除しますか?"
|
"REMOVE_UNUSED_MODELS": "文字起こし機能をオフにします。\n空き容量を増やすために、ダウンロード済みのモデルファイルを削除しますか?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "文字起こしモデル",
|
"WHISPER_MODEL": "文字起こしモデル",
|
||||||
"WHISPER_MODEL_HELP": "使用するWhisperモデル。\nモデルが大きいほど精度は向上しますが、\nVRAMの消費量が増加します。"
|
"WHISPER_MODEL_HELP": "使用するWhisperモデル。\nモデルが大きいほど精度は向上しますが、\nVRAMの消費量が増加します。",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "デフォルトのオーバーレイサイズ",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "新しいオーバーレイをこのスケールで生成します。"
|
||||||
},
|
},
|
||||||
"HELLO": "こんにちは!",
|
"HELLO": "こんにちは!",
|
||||||
"AUDIO": {
|
"AUDIO": {
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "ワーキングセット"
|
"WORKING_SET": "ワーキングセット"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "ダウンロード"
|
"DOWNLOAD": "ダウンロード",
|
||||||
}
|
"SAVED_TO_FAVOURITES": "お気に入りに保存しました",
|
||||||
|
"ADD_TO_FAVOURITES": "お気に入りに追加",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "プリセットを保存してアプリドロワーにピン留めします",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "お気に入りから削除",
|
||||||
|
"EMPTY": "空",
|
||||||
|
"PINNED_APPS": "ピン留めされたアプリケーション"
|
||||||
|
}
|
||||||
|
|
@ -177,7 +177,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "Wyłączasz funkcję zamiany mowy na tekst.\nCzy chcesz usunąć pobrane modele Whisper, aby zwolnić miejsce?"
|
"REMOVE_UNUSED_MODELS": "Wyłączasz funkcję zamiany mowy na tekst.\nCzy chcesz usunąć pobrane modele Whisper, aby zwolnić miejsce?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "Model mowy na tekst",
|
"WHISPER_MODEL": "Model mowy na tekst",
|
||||||
"WHISPER_MODEL_HELP": "Model Whisper do wykorzystania.\nWiększe modele dają dokładniejsze\nwyniki, ale zużywają więcej pamięci VRAM."
|
"WHISPER_MODEL_HELP": "Model Whisper do wykorzystania.\nWiększe modele dają dokładniejsze\nwyniki, ale zużywają więcej pamięci VRAM.",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "Domyślny rozmiar nakładki",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "Twórz nowe nakładki w tej skali."
|
||||||
},
|
},
|
||||||
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
|
"APPLICATION_LAUNCHER": "Uruchamiacz aplikacji",
|
||||||
"APPLICATIONS": "Aplikacje",
|
"APPLICATIONS": "Aplikacje",
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "Aktywny zestaw"
|
"WORKING_SET": "Aktywny zestaw"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "Pobierz"
|
"DOWNLOAD": "Pobierz",
|
||||||
|
"SAVED_TO_FAVOURITES": "Dodano do ulubionych",
|
||||||
|
"ADD_TO_FAVOURITES": "Dodaj do ulubionych",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "Zapisz ustawienie i przypnij je do szuflady aplikacji",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "Usuń z ulubionych",
|
||||||
|
"EMPTY": "Puste",
|
||||||
|
"PINNED_APPS": "Przypięte aplikacje"
|
||||||
}
|
}
|
||||||
|
|
@ -203,7 +203,9 @@
|
||||||
"REMOVE_UNUSED_MODELS": "您正在关闭语音转文本功能。\n是否删除已下载的 Whisper 模型以释放空间?"
|
"REMOVE_UNUSED_MODELS": "您正在关闭语音转文本功能。\n是否删除已下载的 Whisper 模型以释放空间?"
|
||||||
},
|
},
|
||||||
"WHISPER_MODEL": "语音转文本模型",
|
"WHISPER_MODEL": "语音转文本模型",
|
||||||
"WHISPER_MODEL_HELP": "要使用的 Whisper 模型。\n模型越大,结果越准确,\n但消耗的显存 (VRAM) 越多。"
|
"WHISPER_MODEL_HELP": "要使用的 Whisper 模型。\n模型越大,结果越准确,\n但消耗的显存 (VRAM) 越多。",
|
||||||
|
"DEFAULT_OVERLAY_SCALE": "默认叠加层大小",
|
||||||
|
"DEFAULT_OVERLAY_SCALE_HELP": "以该比例生成新的叠加层。"
|
||||||
},
|
},
|
||||||
"APPLICATION_LAUNCHER": "应用启动器",
|
"APPLICATION_LAUNCHER": "应用启动器",
|
||||||
"APPLICATION_STARTED": "应用已启动",
|
"APPLICATION_STARTED": "应用已启动",
|
||||||
|
|
@ -296,5 +298,11 @@
|
||||||
"WATCH": "Watch",
|
"WATCH": "Watch",
|
||||||
"WORKING_SET": "工作集"
|
"WORKING_SET": "工作集"
|
||||||
},
|
},
|
||||||
"DOWNLOAD": "下载"
|
"DOWNLOAD": "下载",
|
||||||
|
"SAVED_TO_FAVOURITES": "已保存至收藏夹",
|
||||||
|
"ADD_TO_FAVOURITES": "添加到收藏",
|
||||||
|
"ADD_TO_FAVOURITES_HINT": "保存预设并将其固定到应用抽屉",
|
||||||
|
"REMOVE_FROM_FAVOURITES": "从收藏中移除",
|
||||||
|
"EMPTY": "空",
|
||||||
|
"PINNED_APPS": "已固定应用程序"
|
||||||
}
|
}
|
||||||
Binary file not shown.
|
|
@ -100,6 +100,7 @@ pub struct InitParams<'a, T> {
|
||||||
pub enum SoundType {
|
pub enum SoundType {
|
||||||
Startup,
|
Startup,
|
||||||
Launch,
|
Launch,
|
||||||
|
Save,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -216,6 +217,7 @@ impl<T: 'static> Frontend<T> {
|
||||||
let path = match sound_type {
|
let path = match sound_type {
|
||||||
SoundType::Startup => "sound/startup.mp3",
|
SoundType::Startup => "sound/startup.mp3",
|
||||||
SoundType::Launch => "sound/app_start.mp3",
|
SoundType::Launch => "sound/app_start.mp3",
|
||||||
|
SoundType::Save => "sound/save.mp3",
|
||||||
};
|
};
|
||||||
|
|
||||||
// try loading a custom sound; if one doesn't exist (or it failed to load), use the built-in asset
|
// try loading a custom sound; if one doesn't exist (or it failed to load), use the built-in asset
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,39 @@ use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::button::{ButtonClickCallback, ComponentButton},
|
components::button::{ButtonClickCallback, ComponentButton},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
|
i18n::Translation,
|
||||||
layout::{WidgetID, WidgetPair},
|
layout::{WidgetID, WidgetPair},
|
||||||
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
|
parser::{Fetchable, ParseDocumentParams, ParserState, TemplateParams},
|
||||||
|
task::Tasks,
|
||||||
};
|
};
|
||||||
use wlx_common::desktop_finder::DesktopEntry;
|
use wlx_common::{config::PinnedApp, desktop_finder::DesktopEntry};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{Frontend, FrontendTasks},
|
frontend::{Frontend, FrontendTasks},
|
||||||
tab::{Tab, TabType},
|
tab::{Tab, TabType},
|
||||||
util::popup_manager::PopupHolder,
|
util::{popup_manager::PopupHolder, wgui_simple},
|
||||||
views::{self},
|
views::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
parser_state: ParserState,
|
||||||
view_launcher: PopupHolder<views::app_launcher::View>,
|
view_launcher: PopupHolder<views::app_launcher::View>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TabApps<T> {
|
#[derive(Clone)]
|
||||||
#[allow(dead_code)]
|
enum Task {
|
||||||
parser_state: ParserState,
|
RefreshPinnedApps,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TabApps<T> {
|
||||||
state: Rc<RefCell<State>>,
|
state: Rc<RefCell<State>>,
|
||||||
app_list: AppList,
|
app_list: AppList,
|
||||||
marker: PhantomData<T>,
|
marker: PhantomData<T>,
|
||||||
|
|
||||||
|
pinned_apps_parent: WidgetID,
|
||||||
|
|
||||||
|
tasks: Tasks<Task>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Tab<T> for TabApps<T> {
|
impl<T> Tab<T> for TabApps<T> {
|
||||||
|
|
@ -34,9 +44,15 @@ impl<T> Tab<T> for TabApps<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
fn update(&mut self, frontend: &mut Frontend<T>, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
|
||||||
let state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
|
|
||||||
self.app_list.tick(frontend, &self.state, &mut self.parser_state)?;
|
self.app_list.tick(frontend, &self.tasks, &mut state, &self.state)?;
|
||||||
|
|
||||||
|
for task in self.tasks.drain() {
|
||||||
|
match task {
|
||||||
|
Task::RefreshPinnedApps => self.refresh_pinned_apps(&mut state, frontend, data)?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state
|
state
|
||||||
.view_launcher
|
.view_launcher
|
||||||
|
|
@ -45,8 +61,85 @@ impl<T> Tab<T> for TabApps<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_entry_from_app_name<'a>(app_id: &str, entries: &'a [DesktopEntry]) -> Option<&'a DesktopEntry> {
|
||||||
|
entries.iter().find(|&entry| *entry.app_id == *app_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TabApps<T> {
|
||||||
|
fn refresh_pinned_apps(&self, state: &mut State, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||||
|
frontend.layout.remove_children(self.pinned_apps_parent);
|
||||||
|
let globals = frontend.globals.clone();
|
||||||
|
|
||||||
|
let mut stale_entries = Vec::<Rc<str>>::new();
|
||||||
|
|
||||||
|
let mut pinned_desktop_entries = Vec::<(PinnedApp, &DesktopEntry)>::new();
|
||||||
|
|
||||||
|
// collect pinned desktop entries
|
||||||
|
{
|
||||||
|
let config = frontend.interface.general_config(data);
|
||||||
|
for pinned_app in &config.pinned_apps {
|
||||||
|
let Some(desktop_entry) = find_entry_from_app_name(&pinned_app.app_id, &self.app_list.all_entries) else {
|
||||||
|
stale_entries.push(pinned_app.app_id.clone());
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
pinned_desktop_entries.push((pinned_app.clone(), desktop_entry));
|
||||||
|
}
|
||||||
|
// cleanup:
|
||||||
|
// remove non-existent app ids from pinned apps
|
||||||
|
config.pinned_apps.retain(|pinned_app| {
|
||||||
|
for app_id in &stale_entries {
|
||||||
|
if *app_id == pinned_app.app_id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if pinned_desktop_entries.is_empty() {
|
||||||
|
wgui_simple::create_label(
|
||||||
|
&mut frontend.layout,
|
||||||
|
self.pinned_apps_parent,
|
||||||
|
Translation::from_translation_key("EMPTY"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mount pinned desktop entries
|
||||||
|
for (pinned_app, desktop_entry) in pinned_desktop_entries {
|
||||||
|
let tooltip_string = format!(
|
||||||
|
"{}\n{}\n{}",
|
||||||
|
pinned_app.compositor_mode.as_ref(),
|
||||||
|
pinned_app.orientation_mode.as_ref(),
|
||||||
|
pinned_app.res_mode.as_ref()
|
||||||
|
);
|
||||||
|
|
||||||
|
let button = mount_entry(
|
||||||
|
frontend,
|
||||||
|
&mut state.parser_state,
|
||||||
|
&doc_params(frontend.globals.clone()),
|
||||||
|
self.pinned_apps_parent,
|
||||||
|
desktop_entry,
|
||||||
|
Some(tooltip_string),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
button.on_click(on_app_click(
|
||||||
|
frontend.tasks.clone(),
|
||||||
|
self.tasks.clone(),
|
||||||
|
globals.clone(),
|
||||||
|
desktop_entry.clone(),
|
||||||
|
self.state.clone(),
|
||||||
|
Some(pinned_app.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct AppList {
|
struct AppList {
|
||||||
//data: Vec<ParserData>,
|
//data: Vec<ParserData>,
|
||||||
|
all_entries: Vec<DesktopEntry>,
|
||||||
entries_to_mount: VecDeque<DesktopEntry>,
|
entries_to_mount: VecDeque<DesktopEntry>,
|
||||||
list_parent: WidgetPair,
|
list_parent: WidgetPair,
|
||||||
prev_category_name: String,
|
prev_category_name: String,
|
||||||
|
|
@ -55,9 +148,11 @@ struct AppList {
|
||||||
// called after the user clicks any desktop entry
|
// called after the user clicks any desktop entry
|
||||||
fn on_app_click(
|
fn on_app_click(
|
||||||
frontend_tasks: FrontendTasks,
|
frontend_tasks: FrontendTasks,
|
||||||
|
tasks: Tasks<Task>,
|
||||||
globals: WguiGlobals,
|
globals: WguiGlobals,
|
||||||
entry: DesktopEntry,
|
entry: DesktopEntry,
|
||||||
state: Rc<RefCell<State>>,
|
state: Rc<RefCell<State>>,
|
||||||
|
pinned_app: Option<PinnedApp>,
|
||||||
) -> ButtonClickCallback {
|
) -> ButtonClickCallback {
|
||||||
Rc::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
views::app_launcher::mount_popup(
|
views::app_launcher::mount_popup(
|
||||||
|
|
@ -65,6 +160,8 @@ fn on_app_click(
|
||||||
globals.clone(),
|
globals.clone(),
|
||||||
entry.clone(),
|
entry.clone(),
|
||||||
state.borrow_mut().view_launcher.clone(),
|
state.borrow_mut().view_launcher.clone(),
|
||||||
|
tasks.make_callback_box(Task::RefreshPinnedApps),
|
||||||
|
pinned_app.clone(),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
@ -81,20 +178,24 @@ fn doc_params(globals: WguiGlobals) -> ParseDocumentParams<'static> {
|
||||||
impl<T> TabApps<T> {
|
impl<T> TabApps<T> {
|
||||||
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID, data: &mut T) -> anyhow::Result<Self> {
|
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID, data: &mut T) -> anyhow::Result<Self> {
|
||||||
let globals = frontend.layout.state.globals.clone();
|
let globals = frontend.layout.state.globals.clone();
|
||||||
|
let parser_state = wgui::parser::parse_from_assets(&doc_params(globals.clone()), &mut frontend.layout, parent_id)?;
|
||||||
|
|
||||||
|
let app_list_parent = parser_state.fetch_widget(&frontend.layout.state, "app_list_parent")?;
|
||||||
|
let pinned_apps_parent = parser_state.fetch_widget(&frontend.layout.state, "pinned_apps_parent")?;
|
||||||
|
|
||||||
let state = Rc::new(RefCell::new(State {
|
let state = Rc::new(RefCell::new(State {
|
||||||
view_launcher: Default::default(),
|
view_launcher: Default::default(),
|
||||||
|
parser_state,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let parser_state = wgui::parser::parse_from_assets(&doc_params(globals.clone()), &mut frontend.layout, parent_id)?;
|
let entries: Vec<_> = frontend
|
||||||
let app_list_parent = parser_state.fetch_widget(&frontend.layout.state, "app_list_parent")?;
|
|
||||||
|
|
||||||
let mut entries_sorted: Vec<_> = frontend
|
|
||||||
.interface
|
.interface
|
||||||
.desktop_finder(data)
|
.desktop_finder(data)
|
||||||
.find_entries()
|
.find_entries()
|
||||||
.into_values()
|
.into_values()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let mut entries_sorted = entries.clone();
|
||||||
entries_sorted.sort_by(|a, b| {
|
entries_sorted.sort_by(|a, b| {
|
||||||
let cat_name_a = get_category_name(a);
|
let cat_name_a = get_category_name(a);
|
||||||
let cat_name_b = get_category_name(b);
|
let cat_name_b = get_category_name(b);
|
||||||
|
|
@ -102,16 +203,21 @@ impl<T> TabApps<T> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let app_list = AppList {
|
let app_list = AppList {
|
||||||
|
all_entries: entries,
|
||||||
entries_to_mount: entries_sorted.drain(..).collect(),
|
entries_to_mount: entries_sorted.drain(..).collect(),
|
||||||
list_parent: app_list_parent,
|
list_parent: app_list_parent,
|
||||||
prev_category_name: String::new(),
|
prev_category_name: String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let tasks = Tasks::<Task>::new();
|
||||||
|
tasks.push(Task::RefreshPinnedApps);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
app_list,
|
app_list,
|
||||||
parser_state,
|
|
||||||
state,
|
state,
|
||||||
|
tasks,
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
|
pinned_apps_parent: pinned_apps_parent.id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,81 +331,93 @@ fn get_category_name(entry: &DesktopEntry) -> &str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppList {
|
fn mount_entry<T>(
|
||||||
fn mount_entry<T>(
|
frontend: &mut Frontend<T>,
|
||||||
&mut self,
|
parser_state: &mut ParserState,
|
||||||
frontend: &mut Frontend<T>,
|
doc_params: &ParseDocumentParams,
|
||||||
parser_state: &mut ParserState,
|
id_parent: WidgetID,
|
||||||
doc_params: &ParseDocumentParams,
|
entry: &DesktopEntry,
|
||||||
entry: &DesktopEntry,
|
tooltip: Option<String>,
|
||||||
) -> anyhow::Result<Rc<ComponentButton>> {
|
) -> anyhow::Result<Rc<ComponentButton>> {
|
||||||
let category_name = get_category_name(entry);
|
{
|
||||||
if category_name != self.prev_category_name {
|
let mut params = TemplateParams::new();
|
||||||
self.prev_category_name = String::from(category_name);
|
|
||||||
let mut params = TemplateParams::new();
|
|
||||||
params.insert("text", category_name);
|
|
||||||
|
|
||||||
parser_state.realize_template(
|
if let Some(tooltip) = tooltip {
|
||||||
doc_params,
|
params.insert_str("tooltip", tooltip);
|
||||||
"CategoryText",
|
};
|
||||||
&mut frontend.layout,
|
|
||||||
self.list_parent.id,
|
|
||||||
params,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
// entry icon
|
||||||
let mut params = TemplateParams::new();
|
params.insert_rc(
|
||||||
|
"src_ext",
|
||||||
|
entry
|
||||||
|
.icon_path
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| "".into(), |icon_path| icon_path.clone()),
|
||||||
|
);
|
||||||
|
|
||||||
// entry icon
|
// entry fallback (question mark) icon
|
||||||
params.insert_rc(
|
params.insert(
|
||||||
"src_ext",
|
"src",
|
||||||
entry
|
if entry.icon_path.is_none() {
|
||||||
.icon_path
|
"dashboard/terminal.svg"
|
||||||
.as_ref()
|
} else {
|
||||||
.map_or_else(|| "".into(), |icon_path| icon_path.clone()),
|
""
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
params.insert("name", &entry.app_name);
|
||||||
|
|
||||||
// entry fallback (question mark) icon
|
let data = parser_state.realize_template(doc_params, "AppEntry", &mut frontend.layout, id_parent, params)?;
|
||||||
params.insert(
|
|
||||||
"src",
|
|
||||||
if entry.icon_path.is_none() {
|
|
||||||
"dashboard/terminal.svg"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
},
|
|
||||||
);
|
|
||||||
params.insert("name", &entry.app_name);
|
|
||||||
|
|
||||||
let data = parser_state.realize_template(
|
data.fetch_component_as::<ComponentButton>("button")
|
||||||
doc_params,
|
|
||||||
"AppEntry",
|
|
||||||
&mut frontend.layout,
|
|
||||||
self.list_parent.id,
|
|
||||||
params,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
data.fetch_component_as::<ComponentButton>("button")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppList {
|
||||||
fn tick<T>(
|
fn tick<T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
frontend: &mut Frontend<T>,
|
frontend: &mut Frontend<T>,
|
||||||
state: &Rc<RefCell<State>>,
|
tasks: &Tasks<Task>,
|
||||||
parser_state: &mut ParserState,
|
state: &mut State,
|
||||||
|
rc_state: &Rc<RefCell<State>>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
|
let parser_state = &mut state.parser_state;
|
||||||
|
|
||||||
// load 30 entries for a single frame at most
|
// load 30 entries for a single frame at most
|
||||||
for _ in 0..30 {
|
for _ in 0..30 {
|
||||||
if let Some(entry) = self.entries_to_mount.pop_front() {
|
if let Some(entry) = self.entries_to_mount.pop_front() {
|
||||||
let globals = frontend.layout.state.globals.clone();
|
let globals = frontend.layout.state.globals.clone();
|
||||||
let button = self.mount_entry(frontend, parser_state, &doc_params(globals.clone()), &entry)?;
|
|
||||||
|
let category_name = get_category_name(&entry);
|
||||||
|
if category_name != self.prev_category_name {
|
||||||
|
self.prev_category_name = String::from(category_name);
|
||||||
|
let mut params = TemplateParams::new();
|
||||||
|
params.insert("text", category_name);
|
||||||
|
|
||||||
|
parser_state.realize_template(
|
||||||
|
&doc_params(globals.clone()),
|
||||||
|
"CategoryText",
|
||||||
|
&mut frontend.layout,
|
||||||
|
self.list_parent.id,
|
||||||
|
params,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let button = mount_entry(
|
||||||
|
frontend,
|
||||||
|
parser_state,
|
||||||
|
&doc_params(globals.clone()),
|
||||||
|
self.list_parent.id,
|
||||||
|
&entry,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
|
||||||
button.on_click(on_app_click(
|
button.on_click(on_app_click(
|
||||||
frontend.tasks.clone(),
|
frontend.tasks.clone(),
|
||||||
|
tasks.clone(),
|
||||||
globals.clone(),
|
globals.clone(),
|
||||||
entry.clone(),
|
entry.clone(),
|
||||||
state.clone(),
|
rc_state.clone(),
|
||||||
|
None,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
use std::{collections::HashMap, rc::Rc, str::FromStr};
|
use std::{collections::HashMap, rc::Rc, str::FromStr};
|
||||||
|
use strum::VariantNames;
|
||||||
use strum::{AsRefStr, EnumString, VariantNames};
|
|
||||||
use wayvr_ipc::packet_client::{PositionMode, WvrProcessLaunchParams};
|
use wayvr_ipc::packet_client::{PositionMode, WvrProcessLaunchParams};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::{button::ComponentButton, checkbox::ComponentCheckbox, radio_group::ComponentRadioGroup},
|
components::{
|
||||||
|
ComponentTrait, button::ComponentButton, checkbox::ComponentCheckbox, radio_group::ComponentRadioGroup,
|
||||||
|
},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, WidgetID},
|
||||||
|
|
@ -12,7 +13,11 @@ use wgui::{
|
||||||
task::Tasks,
|
task::Tasks,
|
||||||
widget::label::WidgetLabel,
|
widget::label::WidgetLabel,
|
||||||
};
|
};
|
||||||
use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, desktop_finder::DesktopEntry};
|
use wlx_common::{
|
||||||
|
config::{AppCompositorMode, AppOrientationMode, AppPosMode, AppResMode, GeneralConfig, PinnedApp},
|
||||||
|
dash_interface::BoxDashInterface,
|
||||||
|
desktop_finder::DesktopEntry,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{FrontendTask, FrontendTasks, SoundType},
|
frontend::{FrontendTask, FrontendTasks, SoundType},
|
||||||
|
|
@ -20,51 +25,23 @@ use crate::{
|
||||||
views::{ViewTrait, ViewUpdateParams},
|
views::{ViewTrait, ViewUpdateParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
|
||||||
enum PosMode {
|
|
||||||
Floating,
|
|
||||||
Anchored,
|
|
||||||
Static,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
|
||||||
enum ResMode {
|
|
||||||
Res1440,
|
|
||||||
Res1080,
|
|
||||||
Res720,
|
|
||||||
Res480,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
|
||||||
enum OrientationMode {
|
|
||||||
Wide,
|
|
||||||
SemiWide,
|
|
||||||
Square,
|
|
||||||
SemiTall,
|
|
||||||
Tall,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
|
||||||
enum CompositorMode {
|
|
||||||
Cage,
|
|
||||||
Native,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum Task {
|
enum Task {
|
||||||
SetCompositor(CompositorMode),
|
SetCompositor(AppCompositorMode),
|
||||||
SetRes(ResMode),
|
SetRes(AppResMode),
|
||||||
SetOrientation(OrientationMode),
|
SetOrientation(AppOrientationMode),
|
||||||
SetAutoStart(bool),
|
SetAutoStart(bool),
|
||||||
Launch,
|
Launch,
|
||||||
|
PinApp,
|
||||||
|
UnpinApp,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct LaunchParams<'a, T> {
|
struct LaunchParams<'a, T> {
|
||||||
application: &'a DesktopEntry,
|
application: &'a DesktopEntry,
|
||||||
compositor_mode: CompositorMode,
|
compositor_mode: AppCompositorMode,
|
||||||
pos_mode: PosMode,
|
pos_mode: AppPosMode,
|
||||||
res_mode: ResMode,
|
res_mode: AppResMode,
|
||||||
orientation_mode: OrientationMode,
|
orientation_mode: AppOrientationMode,
|
||||||
globals: &'a WguiGlobals,
|
globals: &'a WguiGlobals,
|
||||||
frontend_tasks: &'a FrontendTasks,
|
frontend_tasks: &'a FrontendTasks,
|
||||||
interface: &'a mut BoxDashInterface<T>,
|
interface: &'a mut BoxDashInterface<T>,
|
||||||
|
|
@ -88,14 +65,17 @@ pub struct View {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
radio_orientation: Rc<ComponentRadioGroup>,
|
radio_orientation: Rc<ComponentRadioGroup>,
|
||||||
|
|
||||||
compositor_mode: CompositorMode,
|
compositor_mode: AppCompositorMode,
|
||||||
pos_mode: PosMode,
|
pos_mode: AppPosMode,
|
||||||
res_mode: ResMode,
|
res_mode: AppResMode,
|
||||||
orientation_mode: OrientationMode,
|
orientation_mode: AppOrientationMode,
|
||||||
|
|
||||||
|
pinned_app: Option<PinnedApp>,
|
||||||
|
|
||||||
auto_start: bool,
|
auto_start: bool,
|
||||||
|
|
||||||
on_launched: Option<Box<dyn FnOnce()>>,
|
on_close_request: Option<Box<dyn FnOnce()>>,
|
||||||
|
on_app_pins_changed: Option<Box<dyn Fn()>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Params<'a> {
|
pub struct Params<'a> {
|
||||||
|
|
@ -105,7 +85,9 @@ pub struct Params<'a> {
|
||||||
pub parent_id: WidgetID,
|
pub parent_id: WidgetID,
|
||||||
pub config: &'a GeneralConfig,
|
pub config: &'a GeneralConfig,
|
||||||
pub frontend_tasks: &'a FrontendTasks,
|
pub frontend_tasks: &'a FrontendTasks,
|
||||||
pub on_launched: Box<dyn FnOnce()>,
|
pub on_close_request: Box<dyn FnOnce()>,
|
||||||
|
pub on_app_pins_changed: Box<dyn Fn()>,
|
||||||
|
pub pinned_app: Option<PinnedApp>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewTrait for View {
|
impl ViewTrait for View {
|
||||||
|
|
@ -131,6 +113,8 @@ impl View {
|
||||||
let cb_autostart = state.fetch_component_as::<ComponentCheckbox>("cb_autostart")?;
|
let cb_autostart = state.fetch_component_as::<ComponentCheckbox>("cb_autostart")?;
|
||||||
|
|
||||||
let btn_launch = state.fetch_component_as::<ComponentButton>("btn_launch")?;
|
let btn_launch = state.fetch_component_as::<ComponentButton>("btn_launch")?;
|
||||||
|
let btn_pin = state.fetch_component_as::<ComponentButton>("btn_pin")?;
|
||||||
|
let btn_unpin = state.fetch_component_as::<ComponentButton>("btn_unpin")?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_exec")?;
|
let mut label_exec = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_exec")?;
|
||||||
|
|
@ -145,6 +129,16 @@ impl View {
|
||||||
|
|
||||||
tasks.handle_button(&btn_launch, Task::Launch);
|
tasks.handle_button(&btn_launch, Task::Launch);
|
||||||
|
|
||||||
|
if params.pinned_app.is_some() {
|
||||||
|
// "Unpin app"
|
||||||
|
tasks.handle_button(&btn_unpin, Task::UnpinApp);
|
||||||
|
params.layout.remove_widget(btn_pin.base().get_id());
|
||||||
|
} else {
|
||||||
|
// "Pin app"
|
||||||
|
tasks.handle_button(&btn_pin, Task::PinApp);
|
||||||
|
params.layout.remove_widget(btn_unpin.base().get_id());
|
||||||
|
}
|
||||||
|
|
||||||
let id_icon_parent = state.get_widget_id("icon_parent")?;
|
let id_icon_parent = state.get_widget_id("icon_parent")?;
|
||||||
|
|
||||||
// app icon
|
// app icon
|
||||||
|
|
@ -160,28 +154,35 @@ impl View {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let compositor_mode = if params.config.xwayland_by_default {
|
let compositor_mode = match ¶ms.pinned_app {
|
||||||
CompositorMode::Cage
|
Some(pinned_app) => pinned_app.compositor_mode,
|
||||||
} else {
|
None => {
|
||||||
CompositorMode::Native
|
if params.config.xwayland_by_default {
|
||||||
|
AppCompositorMode::Cage
|
||||||
|
} else {
|
||||||
|
AppCompositorMode::Native
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
radio_compositor.set_value_simple(compositor_mode.as_ref())?;
|
|
||||||
tasks.push(Task::SetCompositor(compositor_mode));
|
|
||||||
|
|
||||||
let res_mode = ResMode::Res1080;
|
|
||||||
// TODO: configurable defaults ?
|
// TODO: configurable defaults ?
|
||||||
//radio_res.set_value(res_mode.as_ref())?;
|
let mut res_mode = AppResMode::Res1080;
|
||||||
//tasks.push(Task::SetRes(res_mode));
|
let mut orientation_mode = AppOrientationMode::Wide;
|
||||||
|
let pos_mode = AppPosMode::Anchored;
|
||||||
|
|
||||||
let orientation_mode = OrientationMode::Wide;
|
if let Some(pinned_app) = ¶ms.pinned_app {
|
||||||
// TODO: configurable defaults ?
|
res_mode = pinned_app.res_mode;
|
||||||
//radio_orientation.set_value(orientation_mode.as_ref())?;
|
orientation_mode = pinned_app.orientation_mode;
|
||||||
//tasks.push(Task::SetOrientation(orientation_mode));
|
}
|
||||||
|
|
||||||
let pos_mode = PosMode::Anchored;
|
// update radios
|
||||||
// TODO: configurable defaults ?
|
{
|
||||||
//radio_pos.set_value(pos_mode.as_ref())?;
|
let mut common = params.layout.common();
|
||||||
//tasks.push(Task::SetPos(pos_mode));
|
// TODO: pos_mode is disabled as for now
|
||||||
|
radio_compositor.set_value(&mut common, compositor_mode.as_ref())?;
|
||||||
|
radio_res.set_value(&mut common, res_mode.as_ref())?;
|
||||||
|
radio_orientation.set_value(&mut common, orientation_mode.as_ref())?;
|
||||||
|
}
|
||||||
|
|
||||||
let auto_start = false;
|
let auto_start = false;
|
||||||
|
|
||||||
|
|
@ -189,11 +190,11 @@ impl View {
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, ev| {
|
Box::new(move |_, ev| {
|
||||||
if let Some(mode) = ev.value.and_then(|v| {
|
if let Some(mode) = ev.value.and_then(|v| {
|
||||||
CompositorMode::from_str(&v)
|
AppCompositorMode::from_str(&v)
|
||||||
.inspect_err(|_| {
|
.inspect_err(|_| {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Invalid value for compositor: '{v}'. Valid values are: {:?}",
|
"Invalid value for compositor: '{v}'. Valid values are: {:?}",
|
||||||
ResMode::VARIANTS
|
AppResMode::VARIANTS
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -208,11 +209,11 @@ impl View {
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, ev| {
|
Box::new(move |_, ev| {
|
||||||
if let Some(mode) = ev.value.and_then(|v| {
|
if let Some(mode) = ev.value.and_then(|v| {
|
||||||
ResMode::from_str(&v)
|
AppResMode::from_str(&v)
|
||||||
.inspect_err(|_| {
|
.inspect_err(|_| {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Invalid value for resolution: '{v}'. Valid values are: {:?}",
|
"Invalid value for resolution: '{v}'. Valid values are: {:?}",
|
||||||
ResMode::VARIANTS
|
AppResMode::VARIANTS
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -246,11 +247,11 @@ impl View {
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, ev| {
|
Box::new(move |_, ev| {
|
||||||
if let Some(mode) = ev.value.and_then(|v| {
|
if let Some(mode) = ev.value.and_then(|v| {
|
||||||
OrientationMode::from_str(&v)
|
AppOrientationMode::from_str(&v)
|
||||||
.inspect_err(|_| {
|
.inspect_err(|_| {
|
||||||
log::error!(
|
log::error!(
|
||||||
"Invalid value for orientation: '{v}'. Valid values are: {:?}",
|
"Invalid value for orientation: '{v}'. Valid values are: {:?}",
|
||||||
OrientationMode::VARIANTS
|
AppOrientationMode::VARIANTS
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.ok()
|
.ok()
|
||||||
|
|
@ -290,7 +291,9 @@ impl View {
|
||||||
entry: params.entry,
|
entry: params.entry,
|
||||||
frontend_tasks: params.frontend_tasks.clone(),
|
frontend_tasks: params.frontend_tasks.clone(),
|
||||||
globals: params.globals.clone(),
|
globals: params.globals.clone(),
|
||||||
on_launched: Some(params.on_launched),
|
on_close_request: Some(params.on_close_request),
|
||||||
|
on_app_pins_changed: Some(params.on_app_pins_changed),
|
||||||
|
pinned_app: params.pinned_app,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -307,6 +310,14 @@ impl View {
|
||||||
Task::SetOrientation(mode) => self.orientation_mode = mode,
|
Task::SetOrientation(mode) => self.orientation_mode = mode,
|
||||||
Task::SetAutoStart(auto_start) => self.auto_start = auto_start,
|
Task::SetAutoStart(auto_start) => self.auto_start = auto_start,
|
||||||
Task::Launch => self.action_launch(interface, data),
|
Task::Launch => self.action_launch(interface, data),
|
||||||
|
Task::PinApp => {
|
||||||
|
self.action_pin_app(interface.general_config(data));
|
||||||
|
interface.config_changed(data, Default::default());
|
||||||
|
}
|
||||||
|
Task::UnpinApp => {
|
||||||
|
self.action_unpin_app(interface.general_config(data));
|
||||||
|
interface.config_changed(data, Default::default());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -314,6 +325,49 @@ impl View {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn close(&mut self) {
|
||||||
|
if let Some(c) = self.on_close_request.take() {
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn action_unpin_app(&mut self, config: &mut GeneralConfig) {
|
||||||
|
let Some(pinned_app) = &self.pinned_app else {
|
||||||
|
unreachable!();
|
||||||
|
};
|
||||||
|
self.frontend_tasks.push(FrontendTask::PlaySound(SoundType::Save));
|
||||||
|
config.pinned_apps.retain(|p| p != pinned_app);
|
||||||
|
|
||||||
|
if let Some(c) = &self.on_app_pins_changed {
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn action_pin_app(&mut self, config: &mut GeneralConfig) {
|
||||||
|
self
|
||||||
|
.frontend_tasks
|
||||||
|
.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||||
|
"SAVED_TO_FAVOURITES",
|
||||||
|
)));
|
||||||
|
self.frontend_tasks.push(FrontendTask::PlaySound(SoundType::Save));
|
||||||
|
|
||||||
|
config.pinned_apps.push(PinnedApp {
|
||||||
|
app_id: self.entry.app_id.clone(),
|
||||||
|
compositor_mode: self.compositor_mode,
|
||||||
|
pos_mode: self.pos_mode,
|
||||||
|
orientation_mode: self.orientation_mode,
|
||||||
|
res_mode: self.res_mode,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(c) = &self.on_app_pins_changed {
|
||||||
|
c();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.close();
|
||||||
|
}
|
||||||
|
|
||||||
fn action_launch<T>(&mut self, interface: &mut BoxDashInterface<T>, data: &mut T) {
|
fn action_launch<T>(&mut self, interface: &mut BoxDashInterface<T>, data: &mut T) {
|
||||||
View::try_launch(LaunchParams {
|
View::try_launch(LaunchParams {
|
||||||
application: &self.entry,
|
application: &self.entry,
|
||||||
|
|
@ -326,7 +380,7 @@ impl View {
|
||||||
auto_start: self.auto_start,
|
auto_start: self.auto_start,
|
||||||
interface,
|
interface,
|
||||||
data,
|
data,
|
||||||
on_launched: self.on_launched.take(),
|
on_launched: self.on_close_request.take(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,7 +401,7 @@ impl View {
|
||||||
fn launch<T>(mut params: LaunchParams<T>) -> anyhow::Result<()> {
|
fn launch<T>(mut params: LaunchParams<T>) -> anyhow::Result<()> {
|
||||||
let mut env = Vec::<String>::new();
|
let mut env = Vec::<String>::new();
|
||||||
|
|
||||||
if params.compositor_mode == CompositorMode::Native {
|
if params.compositor_mode == AppCompositorMode::Native {
|
||||||
// This list could be larger, feel free to expand it
|
// This list could be larger, feel free to expand it
|
||||||
env.push("QT_QPA_PLATFORM=wayland".into());
|
env.push("QT_QPA_PLATFORM=wayland".into());
|
||||||
env.push("GDK_BACKEND=wayland".into());
|
env.push("GDK_BACKEND=wayland".into());
|
||||||
|
|
@ -357,19 +411,19 @@ impl View {
|
||||||
}
|
}
|
||||||
|
|
||||||
let args = match params.compositor_mode {
|
let args = match params.compositor_mode {
|
||||||
CompositorMode::Cage => format!("-- {} {}", params.application.exec_path, params.application.exec_args),
|
AppCompositorMode::Cage => format!("-- {} {}", params.application.exec_path, params.application.exec_args),
|
||||||
CompositorMode::Native => params.application.exec_args.to_string(),
|
AppCompositorMode::Native => params.application.exec_args.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let exec = match params.compositor_mode {
|
let exec = match params.compositor_mode {
|
||||||
CompositorMode::Cage => "cage".to_string(),
|
AppCompositorMode::Cage => "cage".to_string(),
|
||||||
CompositorMode::Native => params.application.exec_path.to_string(),
|
AppCompositorMode::Native => params.application.exec_path.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pos_mode = match params.pos_mode {
|
let pos_mode = match params.pos_mode {
|
||||||
PosMode::Floating => PositionMode::Float,
|
AppPosMode::Floating => PositionMode::Float,
|
||||||
PosMode::Anchored => PositionMode::Anchor,
|
AppPosMode::Anchored => PositionMode::Anchor,
|
||||||
PosMode::Static => PositionMode::Static,
|
AppPosMode::Static => PositionMode::Static,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut userdata = HashMap::new();
|
let mut userdata = HashMap::new();
|
||||||
|
|
@ -408,20 +462,20 @@ impl View {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_resolution(res_mode: ResMode, orientation_mode: OrientationMode) -> [u32; 2] {
|
fn calculate_resolution(res_mode: AppResMode, orientation_mode: AppOrientationMode) -> [u32; 2] {
|
||||||
let total_pixels = match res_mode {
|
let total_pixels = match res_mode {
|
||||||
ResMode::Res1440 => 2560 * 1440,
|
AppResMode::Res1440 => 2560 * 1440,
|
||||||
ResMode::Res1080 => 1920 * 1080,
|
AppResMode::Res1080 => 1920 * 1080,
|
||||||
ResMode::Res720 => 1280 * 720,
|
AppResMode::Res720 => 1280 * 720,
|
||||||
ResMode::Res480 => 854 * 480,
|
AppResMode::Res480 => 854 * 480,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ratio_w, ratio_h) = match orientation_mode {
|
let (ratio_w, ratio_h) = match orientation_mode {
|
||||||
OrientationMode::Wide => (16, 9),
|
AppOrientationMode::Wide => (16, 9),
|
||||||
OrientationMode::SemiWide => (3, 2),
|
AppOrientationMode::SemiWide => (3, 2),
|
||||||
OrientationMode::Square => (1, 1),
|
AppOrientationMode::Square => (1, 1),
|
||||||
OrientationMode::SemiTall => (2, 3),
|
AppOrientationMode::SemiTall => (2, 3),
|
||||||
OrientationMode::Tall => (9, 16),
|
AppOrientationMode::Tall => (9, 16),
|
||||||
};
|
};
|
||||||
|
|
||||||
let k = ((total_pixels as f64) / (ratio_w * ratio_h) as f64).sqrt();
|
let k = ((total_pixels as f64) / (ratio_w * ratio_h) as f64).sqrt();
|
||||||
|
|
@ -433,13 +487,20 @@ impl View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: DesktopEntry, popup: PopupHolder<View>) {
|
pub fn mount_popup(
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
entry: DesktopEntry,
|
||||||
|
popup: PopupHolder<View>,
|
||||||
|
on_app_pins_changed: Box<dyn Fn()>,
|
||||||
|
pinned_app: Option<PinnedApp>,
|
||||||
|
) {
|
||||||
frontend_tasks
|
frontend_tasks
|
||||||
.clone()
|
.clone()
|
||||||
.push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
|
.push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
|
||||||
Translation::from_raw_text(&entry.app_name),
|
Translation::from_raw_text(&entry.app_name),
|
||||||
Box::new(move |data| {
|
Box::new(move |data| {
|
||||||
let on_launched = popup.get_close_callback(data.layout);
|
let on_close_request = popup.get_close_callback(data.layout);
|
||||||
let view = View::new(Params {
|
let view = View::new(Params {
|
||||||
entry: entry.clone(),
|
entry: entry.clone(),
|
||||||
globals: &globals,
|
globals: &globals,
|
||||||
|
|
@ -447,7 +508,9 @@ pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: D
|
||||||
parent_id: data.id_content,
|
parent_id: data.id_content,
|
||||||
frontend_tasks: &frontend_tasks,
|
frontend_tasks: &frontend_tasks,
|
||||||
config: data.config,
|
config: data.config,
|
||||||
on_launched,
|
on_close_request,
|
||||||
|
on_app_pins_changed,
|
||||||
|
pinned_app,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
popup.set_view(data.handle, view, None);
|
popup.set_view(data.handle, view, None);
|
||||||
|
|
|
||||||
|
|
@ -86,17 +86,6 @@ impl ComponentRadioGroup {
|
||||||
self.state.borrow().selected.as_ref().and_then(|b| b.get_value())
|
self.state.borrow().selected.as_ref().and_then(|b| b.get_value())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_value_simple(&self, value: &str) -> anyhow::Result<()> {
|
|
||||||
let mut state = self.state.borrow_mut();
|
|
||||||
for radio_box in &state.radio_boxes {
|
|
||||||
if radio_box.get_value().is_some_and(|box_val| &*box_val == value) {
|
|
||||||
state.selected = Some(radio_box.clone());
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
anyhow::bail!("No RadioBox found with value '{value}'")
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_value(&self, common: &mut CallbackDataCommon, value: &str) -> anyhow::Result<()> {
|
pub fn set_value(&self, common: &mut CallbackDataCommon, value: &str) -> anyhow::Result<()> {
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
let mut selected = None;
|
let mut selected = None;
|
||||||
|
|
|
||||||
|
|
@ -32,10 +32,11 @@ pub struct SamplePlayer {
|
||||||
|
|
||||||
fn audio_thread(receiver: Receiver<AudioThreadMessage>) {
|
fn audio_thread(receiver: Receiver<AudioThreadMessage>) {
|
||||||
log::debug!("audio_thread starting");
|
log::debug!("audio_thread starting");
|
||||||
let Ok(stream) = rodio::DeviceSinkBuilder::open_default_sink() else {
|
let Ok(mut stream) = rodio::DeviceSinkBuilder::open_default_sink() else {
|
||||||
log::error!("Failed to open audio stream. Audio will not work.");
|
log::error!("Failed to open audio stream. Audio will not work.");
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
stream.log_on_drop(false);
|
||||||
|
|
||||||
while let Ok(msg) = receiver.recv() {
|
while let Ok(msg) = receiver.recv() {
|
||||||
match msg {
|
match msg {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
use chrono::Offset;
|
use chrono::Offset;
|
||||||
use idmap::IdMap;
|
use idmap::IdMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray, VariantNames};
|
||||||
use wayvr_ipc::packet_client::WvrProcessLaunchParams;
|
use wayvr_ipc::packet_client::WvrProcessLaunchParams;
|
||||||
use wgui::drawing::{self, HsvColor};
|
use wgui::drawing::{self, HsvColor};
|
||||||
|
|
||||||
|
|
@ -151,6 +151,44 @@ pub struct SerializedWindowSet {
|
||||||
pub hidden_overlays: SerializedWindowStates,
|
pub hidden_overlays: SerializedWindowStates,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
|
pub enum AppPosMode {
|
||||||
|
Floating,
|
||||||
|
Anchored,
|
||||||
|
Static,
|
||||||
|
}
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
|
pub enum AppOrientationMode {
|
||||||
|
Wide,
|
||||||
|
SemiWide,
|
||||||
|
Square,
|
||||||
|
SemiTall,
|
||||||
|
Tall,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
|
pub enum AppCompositorMode {
|
||||||
|
Cage,
|
||||||
|
Native,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
|
||||||
|
pub enum AppResMode {
|
||||||
|
Res1440,
|
||||||
|
Res1080,
|
||||||
|
Res720,
|
||||||
|
Res480,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PinnedApp {
|
||||||
|
pub app_id: Rc<str>, // desktop entry app id, for example "libreoffice-draw"
|
||||||
|
pub compositor_mode: AppCompositorMode,
|
||||||
|
pub pos_mode: AppPosMode,
|
||||||
|
pub orientation_mode: AppOrientationMode,
|
||||||
|
pub res_mode: AppResMode,
|
||||||
|
}
|
||||||
|
|
||||||
pub const fn def_pw_tokens() -> PwTokenMap {
|
pub const fn def_pw_tokens() -> PwTokenMap {
|
||||||
AStrMap::new()
|
AStrMap::new()
|
||||||
}
|
}
|
||||||
|
|
@ -401,6 +439,9 @@ pub struct GeneralConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub autostart_apps: Vec<WvrProcessLaunchParams>,
|
pub autostart_apps: Vec<WvrProcessLaunchParams>,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub pinned_apps: Vec<PinnedApp>,
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub last_set: u32,
|
pub last_set: u32,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,11 +78,12 @@ pub trait DashInterface<T> {
|
||||||
fn get_feats(&mut self, data: &mut T) -> InterfaceFeats;
|
fn get_feats(&mut self, data: &mut T) -> InterfaceFeats;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Default, Clone, Copy)]
|
||||||
pub enum ConfigChangeKind {
|
pub enum ConfigChangeKind {
|
||||||
OverlayConfig,
|
OverlayConfig,
|
||||||
EnvironmentBlend,
|
EnvironmentBlend,
|
||||||
/// Marks the config for saving but doesn't notify any components
|
/// Marks the config for saving but doesn't notify any components
|
||||||
|
#[default]
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
struct DesktopEntryOwned {
|
struct DesktopEntryOwned {
|
||||||
|
app_id: String,
|
||||||
exec_path: String,
|
exec_path: String,
|
||||||
exec_args: String,
|
exec_args: String,
|
||||||
app_name: String,
|
app_name: String,
|
||||||
|
|
@ -28,6 +29,7 @@ struct DesktopEntryOwned {
|
||||||
pub struct DesktopEntry {
|
pub struct DesktopEntry {
|
||||||
pub exec_path: Rc<str>,
|
pub exec_path: Rc<str>,
|
||||||
pub exec_args: Rc<str>,
|
pub exec_args: Rc<str>,
|
||||||
|
pub app_id: Rc<str>,
|
||||||
pub app_name: Rc<str>,
|
pub app_name: Rc<str>,
|
||||||
pub icon_path: Option<Rc<str>>,
|
pub icon_path: Option<Rc<str>>,
|
||||||
pub categories: Vec<Rc<str>>,
|
pub categories: Vec<Rc<str>>,
|
||||||
|
|
@ -36,6 +38,7 @@ pub struct DesktopEntry {
|
||||||
impl From<DesktopEntryOwned> for DesktopEntry {
|
impl From<DesktopEntryOwned> for DesktopEntry {
|
||||||
fn from(value: DesktopEntryOwned) -> Self {
|
fn from(value: DesktopEntryOwned) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
app_id: value.app_id.into(),
|
||||||
exec_path: value.exec_path.into(),
|
exec_path: value.exec_path.into(),
|
||||||
exec_args: value.exec_args.into(),
|
exec_args: value.exec_args.into(),
|
||||||
app_name: value.app_name.into(),
|
app_name: value.app_name.into(),
|
||||||
|
|
@ -242,8 +245,9 @@ impl DesktopFinder {
|
||||||
known_files.insert(file_name.to_string());
|
known_files.insert(file_name.to_string());
|
||||||
|
|
||||||
entries.insert(
|
entries.insert(
|
||||||
app_id,
|
app_id.clone(),
|
||||||
DesktopEntryOwned {
|
DesktopEntryOwned {
|
||||||
|
app_id,
|
||||||
app_name: String::from(app_name),
|
app_name: String::from(app_name),
|
||||||
exec_path: String::from(exec_path),
|
exec_path: String::from(exec_path),
|
||||||
exec_args: exec_args.join(" "),
|
exec_args: exec_args.join(" "),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue