Merge remote-tracking branch 'origin/main' into next-dash-interface
[skip ci]
This commit is contained in:
15
wlx-overlay-s/Cargo.lock
generated
15
wlx-overlay-s/Cargo.lock
generated
@@ -5551,19 +5551,6 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayvr_ipc"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/olekolek1000/wayvr-ipc.git?rev=a72587d23f3bb8624d9aeb1f13c0a21e65350f51#a72587d23f3bb8624d9aeb1f13c0a21e65350f51"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.77"
|
||||
@@ -6206,7 +6193,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"wayland-client",
|
||||
"wayland-egl",
|
||||
"wayvr_ipc",
|
||||
"wayvr-ipc",
|
||||
"wgui",
|
||||
"winit",
|
||||
"wlx-capture",
|
||||
|
||||
@@ -97,6 +97,7 @@ uuid = { version = "1.19.0", features = ["v4", "fast-rng"], optional = true }
|
||||
wayland-client = { workspace = true, optional = true }
|
||||
wayland-egl = { version = "0.32.8", optional = true }
|
||||
bytes = { version = "1.11.0", optional = true }
|
||||
wayvr-ipc = { path = "../wayvr-ipc", default-features = false, optional = true }
|
||||
rust-embed = { workspace = true }
|
||||
signal-hook = "0.3.18"
|
||||
################################
|
||||
@@ -121,5 +122,6 @@ wayvr = [
|
||||
"dep:wayland-client",
|
||||
"dep:wayland-egl",
|
||||
"dep:bytes",
|
||||
"dep:wayvr-ipc",
|
||||
]
|
||||
as-raw-xcb-connection = []
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</template>
|
||||
|
||||
<template name="TopButtonDanger">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _press="${press}" _release="${release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<Button macro="button_style" tooltip="${tooltip}" _long_release="${long_release}" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<sprite width="48" height="48" src="${src}" />
|
||||
</Button>
|
||||
</template>
|
||||
@@ -39,7 +39,7 @@
|
||||
<rectangle padding="16" gap="8" round="32" color="~color_bg" border="2" border_color="~color_accent" justify_content="center">
|
||||
<div flex_direction="column" gap="8">
|
||||
<div flex_direction="row" gap="4">
|
||||
<TopButton sticky="1" id="top_lock" src="edit/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" press="::EditModeToggleLock" />
|
||||
<TopButton sticky="0" id="top_lock" src="edit/lock_open.svg" tooltip="EDIT_MODE.LOCK_INTERACTION" press="::EditModeToggleLock" />
|
||||
<TopButton sticky="1" id="top_grab" src="edit/disable-grab.svg" tooltip="EDIT_MODE.DISABLE_GRAB" press="::EditModeToggleGrab" />
|
||||
<TopButton sticky="0" id="top_pos" src="edit/anchor.svg" tooltip="EDIT_MODE.POSITIONING" press="::EditModeTab pos" />
|
||||
<TopButton sticky="0" id="top_alpha" src="edit/fade.svg" tooltip="EDIT_MODE.OPACITY" press="::EditModeTab alpha" />
|
||||
@@ -48,7 +48,7 @@
|
||||
<TopButton sticky="0" id="top_mouse" src="edit/normal.svg" tooltip="EDIT_MODE.MOUSE.TITLE" press="::EditModeTab mouse" />
|
||||
<!-- TopButton sticky="0" id="top_move" src="edit/move-all.svg" tooltip="EDIT_MODE.MOVE_PRESS_AND_DRAG" / -->
|
||||
<!-- TopButton sticky="0" id="top_resize" src="edit/resize.svg" tooltip="EDIT_MODE.RESIZE_PRESS_AND_DRAG" / -->
|
||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" press="::EditModeDeletePress" release="::EditModeDeleteRelease" />
|
||||
<TopButtonDanger src="edit/delete.svg" tooltip="EDIT_MODE.DELETE" long_release="::EditModeDelete" />
|
||||
<div width="8" height="100%" />
|
||||
<TopButtonFaded src="watch/edit.svg" tooltip="EDIT_MODE.LEAVE" press="::EditToggle" />
|
||||
</div>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<template name="KeySpecial">
|
||||
<div macro="keycap_div">
|
||||
<rectangle id="${id}" macro="keycap_rect">
|
||||
<sprite width="32" height="32" src="keyboard/${text}.svg" />
|
||||
<sprite color="~color_text" width="32" height="32" src="keyboard/${text}.svg" />
|
||||
</rectangle>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -34,8 +34,8 @@ All overlays are listed on bottom row.
|
||||
</template>
|
||||
|
||||
<template name="Overlay">
|
||||
<Button macro="button_style" id="overlay_${idx}" sticky="1"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}"
|
||||
<Button macro="button_style" id="overlay_${idx}"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::SingleSetOverlayToggle ${idx}" _long_release="::SingleSetOverlayReset ${idx}"
|
||||
align_items="center"
|
||||
height="40">
|
||||
<sprite id="overlay_${idx}_sprite" src_builtin="${src}" width="32" height="32" />
|
||||
@@ -111,13 +111,13 @@ All overlays are listed on bottom row.
|
||||
|
||||
<!-- Bottom buttons -->
|
||||
<div id="toolbox-condensed" gap="6" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
|
||||
<Button id="btn_dashboard" height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
|
||||
<Button id="btn_dashboard" height="40" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" >
|
||||
<sprite color="~set_color" width="32" height="32" src="watch/wayvr_dashboard_mono.svg" />
|
||||
</Button>
|
||||
<Button id="btn_edit_mode" height="40" macro="button_style" _press="::EditToggle" tooltip="WATCH.EDIT_MODE" tooltip_side="top">
|
||||
<sprite color="~set_color" width="32" height="32" src="watch/edit.svg" />
|
||||
</Button>
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" >
|
||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||
</Button>
|
||||
<!-- Src here may be changed, but maintain `OverlayCategory` order: Panel, Screen, Mirror, WayVR -->
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</template>
|
||||
|
||||
<template name="Overlay">
|
||||
<Button macro="button_style" id="overlay_${idx}" sticky="1"
|
||||
<Button macro="button_style" id="overlay_${idx}"
|
||||
tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::EditModeOverlayToggle ${idx}"
|
||||
align_items="center"
|
||||
height="40">
|
||||
@@ -120,7 +120,7 @@
|
||||
</div>
|
||||
<div flex_direction="column" align_items="center" justify_content="center">
|
||||
<div id="toolbox" gap="8" width="100%" max_width="400" flex_direction="row" flex_wrap="wrap">
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" sticky="1" >
|
||||
<Button id="btn_keyboard" height="40" macro="button_style" tooltip="WATCH.TOGGLE_FOR_CURRENT_SET" _press="::OverlayToggle kbd" >
|
||||
<sprite src_builtin="watch/keyboard.svg" width="32" height="32" />
|
||||
<label translation="EDIT_MODE.KEYBOARD" size="18" />
|
||||
</Button>
|
||||
@@ -136,11 +136,11 @@
|
||||
|
||||
<!-- Bottom buttons -->
|
||||
<div flex_direction="row" gap="4">
|
||||
<Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" sticky="1" >
|
||||
<Button id="btn_dashboard" macro="button_style" _press="::DashToggle" tooltip="WATCH.DASHBOARD" tooltip_side="top" >
|
||||
<sprite color="~set_color" width="40" height="40" src="watch/wayvr_dashboard_mono.svg" />
|
||||
</Button>
|
||||
<div id="edit_delete" display="none">
|
||||
<Button macro="button_style" _press="::EditModeDeleteDown" _release="::EditModeDeleteUp" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<Button macro="button_style" _long_release="::EditModeDeleteSet" tooltip="WATCH.LONG_PRESS_TO_DELETE_SET" tooltip_side="top" border_color="~color_danger_translucent" color="~color_danger_5" color2="~color_danger_1">
|
||||
<sprite color="~set_color" width="40" height="40" src="edit/delete.svg" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,43 @@
|
||||
"SPLIT_TOP_BOTTOM": "OBEN→UNTEN",
|
||||
"SPLIT_BOTTOM_TOP": "UNTEN→OBEN"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"STEREO_3D_MODE_": {},
|
||||
"ALIGN_TO_HMD": "An HMD ausrichten",
|
||||
"MOUSE": {
|
||||
"TITLE": "Mauskorrekturen",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Wenn der Cursor auf einem völlig anderen Bildschirm verschwindet,\nwurden die Bildschirme wahrscheinlich falsch ausgewählt. Siehe Lesezeichen.",
|
||||
"NORMAL": "Normal",
|
||||
"FLIPPED": "Umgedreht",
|
||||
"FLIP90": "Um 90° gedreht",
|
||||
"FLIP180": "Um 180° gedreht",
|
||||
"FLIP270": "Um 270° gedreht",
|
||||
"ROTATE90": "Um 90° gedreht",
|
||||
"ROTATE180": "Um 180° gedreht",
|
||||
"ROTATE270": "Um 270° gedreht"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Deaktiviert"
|
||||
}
|
||||
"DISABLED": "Deaktiviert",
|
||||
"DEFAULT": "Standard",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Entfernung anpassen",
|
||||
"ADJUST_SIZE": "Größe anpassen",
|
||||
"UNRESTRICTED_MOVEMENT": "Unbeschränkte Bewegung",
|
||||
"GRABBING_WATCH": "Um die Hände zu vertauschen, bewegen Sie die Uhr vor sich und greifen Sie sie mit der anderen Hand.",
|
||||
"GRABBING_STATIC": "Dieses Overlay ist statisch und bleibt an Ort und Stelle, ignoriert die Neu-Zentrierung.",
|
||||
"GRABBING_ANCHORED": "Festgelegte Overlays bewegen sich alle zusammen. Trennen Sie ein einzelnes Fenster, indem Sie es mit der anderen Hand greifen, während Sie den Anker weiterhin greifen.",
|
||||
"GRABBING_ANCHORED_EDIT": "Diese Overlay-Darstellung bleibt an der Mittelmarkierung verankert.",
|
||||
"GRABBING_FLOATING": "Diese Overlay-Darstellung ist frei schwebend und bleibt an Ort und Stelle, es sei denn, sie wird neu zentriert.",
|
||||
"GRABBING_FOLLOW": "Dieses Overlay wird dem Gerät folgen, an das es angehängt ist."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Benachrichtigung",
|
||||
"ERROR": "Fehler",
|
||||
"CANNOT_REMOVE_SET": "Satz kann nicht entfernt werden!",
|
||||
"NO_SET_SELECTED": "Kein Set ausgewählt.",
|
||||
"LAST_EXISTING_SET": "Dies ist das letzte vorhandene Set.",
|
||||
"EMPTY_SET": "Leeres Set!",
|
||||
"LETS_ADD_OVERLAYS": "Lass uns ein paar Overlays von der Uhr hinzufügen!",
|
||||
"FIXING_FLOOR": "Boden wird in 5 Sekunden fixiert...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Lege einen Controller auf den Boden!"
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,12 @@
|
||||
"WRONG_SCREEN_SELECTION_HELP": "If the cursor moves on a completely different screen,\nthe screens were likely selected wrong. See readme.",
|
||||
"NORMAL": "Normal",
|
||||
"ROTATE90": "Rotated 90°",
|
||||
"ROTATE180": "Rotated 170°",
|
||||
"ROTATE180": "Rotated 180°",
|
||||
"ROTATE270": "Rotated 270°",
|
||||
"FLIPPED": "Flipped",
|
||||
"ROTATE90": "Flipped 90°",
|
||||
"ROTATE180": "Flipped 170°",
|
||||
"ROTATE270": "Flipped 270°"
|
||||
"FLIP90": "Flipped 90°",
|
||||
"FLIP180": "Flipped 180°",
|
||||
"FLIP270": "Flipped 270°"
|
||||
}
|
||||
},
|
||||
"GRAB": {
|
||||
@@ -58,6 +58,17 @@
|
||||
"GRABBING_FLOATING": "This overlay is Floating and will stay in place, unless recentered.",
|
||||
"GRABBING_FOLLOW": "This overlay will follow the device it is attached to."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Notification",
|
||||
"ERROR": "Error",
|
||||
"CANNOT_REMOVE_SET": "Cannot remove set!",
|
||||
"NO_SET_SELECTED": "No set is selected.",
|
||||
"LAST_EXISTING_SET": "This is the last existing set.",
|
||||
"EMPTY_SET": "Empty set!",
|
||||
"LETS_ADD_OVERLAYS": "Let's add some overlays from the watch!",
|
||||
"FIXING_FLOOR": "Fixing floor in 5 seconds...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Place one controller on the floor!"
|
||||
},
|
||||
"WATCH": {
|
||||
"ADD_NEW_SET": "Add a new set",
|
||||
"CLEANUP_MIRRORS": "Remove mirrors that are\nnot currently visible",
|
||||
|
||||
@@ -45,7 +45,43 @@
|
||||
"SPLIT_TOP_BOTTOM": "ARRIBA→ABAJO",
|
||||
"SPLIT_BOTTOM_TOP": "ABAJO→ARRIBA"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"STEREO_3D_MODE_": {},
|
||||
"ALIGN_TO_HMD": "Alinear al HMD",
|
||||
"MOUSE": {
|
||||
"TITLE": "Correcciones del ratón",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Si el cursor se mueve en una pantalla completamente diferente,\nes probable que las pantallas se hayan seleccionado incorrectamente. Consulta el archivo readme.",
|
||||
"NORMAL": "Normal",
|
||||
"FLIPPED": "Invertido",
|
||||
"FLIP90": "Girado 90°",
|
||||
"FLIP180": "Invertido 180°",
|
||||
"FLIP270": "Invertido 270°",
|
||||
"ROTATE90": "Rotado 90°",
|
||||
"ROTATE180": "Rotado 180°",
|
||||
"ROTATE270": "Rotado 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Deshabilitado"
|
||||
"DISABLED": "Deshabilitado",
|
||||
"DEFAULT": "Por defecto",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Ajustar distancia",
|
||||
"ADJUST_SIZE": "Ajustar tamaño",
|
||||
"UNRESTRICTED_MOVEMENT": "Movimiento sin restricciones",
|
||||
"GRABBING_WATCH": "Para intercambiar las manos, mueve el reloj al frente y agrárralo con la otra mano.",
|
||||
"GRABBING_STATIC": "Esta superposición es estática y permanecerá en su lugar, ignorando el recentrado.",
|
||||
"GRABBING_ANCHORED": "Las superposiciones ancladas se mueven todas juntas. Separa una ventana individual agarrándola con la otra mano mientras aún sujetas el ancla.",
|
||||
"GRABBING_ANCHORED_EDIT": "Esta superposición permanecerá anclada al marcador central.",
|
||||
"GRABBING_FLOATING": "Esta superposición es flotante y permanecerá en su lugar a menos que se recentre.",
|
||||
"GRABBING_FOLLOW": "Esta superposición seguirá el dispositivo al que está adjunta."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Notificación",
|
||||
"ERROR": "Error",
|
||||
"CANNOT_REMOVE_SET": "¡No se puede eliminar el conjunto!",
|
||||
"NO_SET_SELECTED": "No se ha seleccionado ningún conjunto.",
|
||||
"LAST_EXISTING_SET": "Este es el último conjunto existente.",
|
||||
"EMPTY_SET": "¡Conjunto vacío!",
|
||||
"LETS_ADD_OVERLAYS": "¡Añadamos algunos overlays desde el reloj!",
|
||||
"FIXING_FLOOR": "Fijando el suelo en 5 segundos...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "¡Coloca un mando en el suelo!"
|
||||
}
|
||||
}
|
||||
@@ -16,28 +16,27 @@
|
||||
"LONG_PRESS_TO_DELETE_SET": "長押しでセットを削除"
|
||||
},
|
||||
"EDIT_MODE": {
|
||||
"ADJUST_CURVATURE": "曲率を調整する",
|
||||
"ALPHA_BLEND_MODE": "アルファブレンドモード",
|
||||
"BLENDING_ADDITIVE": "加算",
|
||||
"ADJUST_CURVATURE": "曲率の調整",
|
||||
"ALPHA_BLEND_MODE": "アルファブレンド",
|
||||
"BLENDING_ADDITIVE": "加算モード",
|
||||
"CURVATURE": "曲率",
|
||||
"DELETE": "削除",
|
||||
"HINT_POINT_WINDOW": "ウィンドウをタップしてそのパラメータを変更します。\n完了したら、右側のボタンで編集モードを終了してください。",
|
||||
"LEAVE": "編集モードを終了",
|
||||
"LOCK_INTERACTION": "インタラクションをロック",
|
||||
"HINT_POINT_WINDOW": "ウィンドウをタップしてそのパラメータを変更できます。\n右側のボタンで編集モードを終了できます。",
|
||||
"LEAVE": "編集モードを終了する",
|
||||
"LOCK_INTERACTION": "インタラクションを無効化",
|
||||
"DISABLE_GRAB": "グラブを無効化",
|
||||
"MOVE_PRESS_AND_DRAG": "移動(押してドラッグ)",
|
||||
"OPACITY": "不透明度",
|
||||
"POSITIONING": "位置調整",
|
||||
"POSITIONING": "位置付け",
|
||||
"RESIZE_PRESS_AND_DRAG": "サイズ変更(押してドラッグ)",
|
||||
"POS_STATIC": "固定:その場に留まり、再センタリングされません。",
|
||||
"POS_ANCHORED": "アンカー:他のセット要素と一緒に移動します。デフォルト。",
|
||||
"POS_FLOATING": "フローティング:その場に留まり、表示されるときに再センタリングされます。",
|
||||
"POS_STATIC": "固定:置かれた場所に留まり、再センタリングされません。",
|
||||
"POS_ANCHORED": "アンカー:セット内の他のウィンドウと共に移動します。デフォルト。",
|
||||
"POS_FLOATING": "フローティング:置かれた場所に留まるが、表示される時に再センタリングされます。",
|
||||
"POS_HMD": "HMD に追従する。",
|
||||
"POS_HAND_L": "左手に追従する。",
|
||||
"POS_HAND_R": "右手に追従する。",
|
||||
"INTERPOLATION": "補間",
|
||||
"INTERPOLATION": "スムージング",
|
||||
"KEYBOARD": "キーボード",
|
||||
"STEREO_3D_MODE_A": {},
|
||||
"STEREO_3D_MODE": {
|
||||
"TITLE": "ステレオ3Dモード",
|
||||
"SPLIT_LEFT_RIGHT": "左→右",
|
||||
@@ -45,7 +44,42 @@
|
||||
"SPLIT_TOP_BOTTOM": "上→下",
|
||||
"SPLIT_BOTTOM_TOP": "下→上"
|
||||
},
|
||||
"STEREO_3D_MODE_": {}
|
||||
"ALIGN_TO_HMD": "常にHMDの方を向く",
|
||||
"MOUSE": {
|
||||
"TITLE": "マウスの修正",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "カーソルが全く別のスクリーンで動く場合、\nスクリーンが正しく選択されていない可能性があります。\nマニュアルをご確認ください。",
|
||||
"NORMAL": "通常",
|
||||
"FLIPPED": "ミラー",
|
||||
"FLIP90": "ミラー 90°",
|
||||
"FLIP180": "ミラー 180°",
|
||||
"FLIP270": "ミラー 270°",
|
||||
"ROTATE90": "回転 90°",
|
||||
"ROTATE180": "回転 180°",
|
||||
"ROTATE270": "回転 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "無効"
|
||||
"DISABLED": "無効",
|
||||
"DEFAULT": "デフォルト",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "距離を調整",
|
||||
"ADJUST_SIZE": "サイズ調整",
|
||||
"UNRESTRICTED_MOVEMENT": "配置制限なし",
|
||||
"GRABBING_WATCH": "手を入れ替えるにはウォッチを前に移動させて、もう一方の手で掴んでください。",
|
||||
"GRABBING_STATIC": "このオーバーレイは固定であり、リセンターを無視してその場に留まります。",
|
||||
"GRABBING_ANCHORED": "アンカーされたオーバーレイはすべて一緒に移動します。もう一方の手で掴んでアンカーを掴んだまま、単一のウィンドウを分離できます。",
|
||||
"GRABBING_ANCHORED_EDIT": "このオーバーレイは、センター・マーカーに固定されます。",
|
||||
"GRABBING_FLOATING": "このオーバーレイはフローティングのため、リセンターまではその場所に留まります。",
|
||||
"GRABBING_FOLLOW": "このオーバーレイは、接続されているデバイスに追従します。"
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "通知",
|
||||
"ERROR": "エラー",
|
||||
"CANNOT_REMOVE_SET": "セットを削除できません!",
|
||||
"NO_SET_SELECTED": "セットが選択されていません。",
|
||||
"LAST_EXISTING_SET": "これが最後の設定です。",
|
||||
"EMPTY_SET": "空のセットです!",
|
||||
"LETS_ADD_OVERLAYS": "ウォッチからオーバーレイを追加しましょう!",
|
||||
"FIXING_FLOOR": "5秒後にフロアを固定します...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "コントローラーを床に置いてください!"
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,43 @@
|
||||
"SPLIT_RIGHT_LEFT": "PRAWO→LEWO",
|
||||
"SPLIT_TOP_BOTTOM": "GÓRA→DÓŁ",
|
||||
"TITLE": "Tryb stereoskopowy 3D"
|
||||
},
|
||||
"ALIGN_TO_HMD": "Dopasuj do HMD",
|
||||
"MOUSE": {
|
||||
"TITLE": "Naprawa myszy",
|
||||
"WRONG_SCREEN_SELECTION_HELP": "Jeśli kursor porusza się na zupełnie innym ekranie,\nekrany zostały prawdopodobnie nieprawidłowo wybrane. Zobacz dokument README.",
|
||||
"NORMAL": "Normalny",
|
||||
"ROTATE90": "Obrócony o 90°",
|
||||
"ROTATE180": "Obrócony o 180°",
|
||||
"ROTATE270": "Obrócony o 270°",
|
||||
"FLIPPED": "Odwrócony",
|
||||
"FLIP90": "Odwrócony o 90°",
|
||||
"FLIP180": "Odwrócony o 180°",
|
||||
"FLIP270": "Odwrócony o 270°"
|
||||
}
|
||||
},
|
||||
"DISABLED": "Wyłączone"
|
||||
"DISABLED": "Wyłączone",
|
||||
"DEFAULT": "Domyślny",
|
||||
"GRAB": {
|
||||
"ADJUST_DISTANCE": "Dostosuj odległość",
|
||||
"ADJUST_SIZE": "Dostosuj rozmiar",
|
||||
"UNRESTRICTED_MOVEMENT": "Nieograniczony ruch",
|
||||
"GRABBING_WATCH": "Aby zamienić ręce, przenieś zegarek na przód i chwyć go drugą ręką.",
|
||||
"GRABBING_STATIC": "Ta nakładka jest statyczna i pozostanie na swoim miejscu, ignorując centrowanie.",
|
||||
"GRABBING_ANCHORED": "Przymocowane nakładki poruszają się razem. Aby oddzielić pojedyncze okno, chwyć je drugą ręką, podczas gdy nadal trzymasz kotwicę.",
|
||||
"GRABBING_ANCHORED_EDIT": "Ta nakładka pozostanie zakotwiczona do znacznika centralnego.",
|
||||
"GRABBING_FLOATING": "Ta nakładka jest zawieszony i pozostanie na miejscu, chyba że zostanie wycentrowany.",
|
||||
"GRABBING_FOLLOW": "Ta nakładka będzie śledzić urządzenie, do którego jest dołączona."
|
||||
},
|
||||
"TOAST": {
|
||||
"DEFAULT_TITLE": "Powiadomienie",
|
||||
"ERROR": "Błąd",
|
||||
"CANNOT_REMOVE_SET": "Nie można usunąć zestawu!",
|
||||
"NO_SET_SELECTED": "Nie wybrano żadnego zestawu.",
|
||||
"LAST_EXISTING_SET": "To jest ostatni istniejący zestaw.",
|
||||
"EMPTY_SET": "Pusty zestaw!",
|
||||
"LETS_ADD_OVERLAYS": "Dodajmy kilka nakładek z zegarka!",
|
||||
"FIXING_FLOOR": "Naprawianie podłogi za 5 sekund...",
|
||||
"ONE_CONTROLLER_ON_FLOOR": "Umieść jeden kontroler na podłodze!"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A};
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3a};
|
||||
use ovr_overlay::{
|
||||
chaperone_setup::ChaperoneSetupManager,
|
||||
compositor::CompositorManager,
|
||||
@@ -58,12 +58,12 @@ impl PlayspaceMover {
|
||||
&mut self,
|
||||
chaperone_mgr: &mut ChaperoneSetupManager,
|
||||
overlays: &mut OverlayWindowManager<OpenVrOverlayData>,
|
||||
state: &AppState,
|
||||
app: &AppState,
|
||||
) {
|
||||
let universe = self.universe.clone();
|
||||
|
||||
if let Some(data) = self.rotate.as_mut() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_rotate {
|
||||
self.rotate = None;
|
||||
log::info!("End space rotate");
|
||||
@@ -71,7 +71,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
let new_hand =
|
||||
Quat::from_affine3(&(data.pose * state.input_state.pointers[data.hand].raw_pose));
|
||||
Quat::from_affine3(&(data.pose * app.input_state.pointers[data.hand].raw_pose));
|
||||
|
||||
let dq = new_hand * data.hand_pose.conjugate();
|
||||
let rel_y = f32::atan2(
|
||||
@@ -80,8 +80,8 @@ impl PlayspaceMover {
|
||||
);
|
||||
|
||||
let mut space_transform = Affine3A::from_rotation_y(rel_y);
|
||||
let offset = (space_transform.transform_vector3a(state.input_state.hmd.translation)
|
||||
- state.input_state.hmd.translation)
|
||||
let offset = (space_transform.transform_vector3a(app.input_state.hmd.translation)
|
||||
- app.input_state.hmd.translation)
|
||||
* -1.0;
|
||||
let mut overlay_transform = Affine3A::from_rotation_y(-rel_y);
|
||||
|
||||
@@ -97,7 +97,7 @@ impl PlayspaceMover {
|
||||
set_working_copy(&universe, chaperone_mgr, &data.pose);
|
||||
chaperone_mgr.commit_working_copy(EChaperoneConfigFile::EChaperoneConfigFile_Live);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_rotate {
|
||||
let Some(mat) = get_working_copy(&universe, chaperone_mgr) else {
|
||||
log::warn!("Can't space rotate - failed to get zero pose");
|
||||
@@ -117,7 +117,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(data) = self.drag.as_mut() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_drag {
|
||||
self.drag = None;
|
||||
log::info!("End space drag");
|
||||
@@ -126,9 +126,13 @@ impl PlayspaceMover {
|
||||
|
||||
let new_hand = data
|
||||
.pose
|
||||
.transform_point3a(state.input_state.pointers[data.hand].raw_pose.translation);
|
||||
let relative_pos =
|
||||
(new_hand - data.hand_pose) * state.session.config.space_drag_multiplier;
|
||||
.transform_point3a(app.input_state.pointers[data.hand].raw_pose.translation);
|
||||
|
||||
let relative_pos = if app.session.config.space_drag_unlocked {
|
||||
new_hand - data.hand_pose
|
||||
} else {
|
||||
vec3a(0., new_hand.y - data.hand_pose.y, 0.)
|
||||
} * app.session.config.space_drag_multiplier;
|
||||
|
||||
if relative_pos.length_squared() > 1000.0 {
|
||||
log::warn!("Space drag too fast, ignoring");
|
||||
@@ -156,7 +160,7 @@ impl PlayspaceMover {
|
||||
set_working_copy(&universe, chaperone_mgr, &data.pose);
|
||||
chaperone_mgr.commit_working_copy(EChaperoneConfigFile::EChaperoneConfigFile_Live);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_drag {
|
||||
let Some(mat) = get_working_copy(&universe, chaperone_mgr) else {
|
||||
log::warn!("Can't space drag - failed to get zero pose");
|
||||
|
||||
@@ -11,7 +11,6 @@ use std::{
|
||||
};
|
||||
|
||||
use wgui::gfx::{
|
||||
WGfx,
|
||||
cmd::WGfxClearMode,
|
||||
pass::WGfxPass,
|
||||
pipeline::{WGfxPipeline, WPipelineCreateInfo},
|
||||
@@ -49,8 +48,6 @@ static COLORS: [[f32; 6]; 5] = {
|
||||
pub(super) struct LinePool {
|
||||
lines: IdMap<usize, LineContainer>,
|
||||
pipeline: Arc<WGfxPipeline<Vert2Uv>>,
|
||||
pass: WGfxPass<Vert2Uv>,
|
||||
buf_color: Subbuffer<[f32]>,
|
||||
}
|
||||
|
||||
impl LinePool {
|
||||
@@ -61,13 +58,22 @@ impl LinePool {
|
||||
WPipelineCreateInfo::new(app.gfx.surface_format),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
pipeline,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn allocate(&mut self, xr: &XrState, app: &AppState) -> anyhow::Result<usize> {
|
||||
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let buf_color = app
|
||||
.gfx
|
||||
.empty_buffer(BufferUsage::TRANSFER_DST | BufferUsage::UNIFORM_BUFFER, 6)?;
|
||||
|
||||
let set0 = pipeline.buffer(0, buf_color.clone())?;
|
||||
let set0 = self.pipeline.buffer(0, buf_color.clone())?;
|
||||
|
||||
let pass = pipeline.create_pass(
|
||||
let pass = self.pipeline.create_pass(
|
||||
[1.0, 1.0],
|
||||
app.gfx_extras.quad_verts.clone(),
|
||||
0..4,
|
||||
@@ -76,22 +82,13 @@ impl LinePool {
|
||||
&Default::default(),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
lines: IdMap::new(),
|
||||
pipeline,
|
||||
pass,
|
||||
buf_color,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn allocate(&mut self, xr: &XrState, gfx: Arc<WGfx>) -> anyhow::Result<usize> {
|
||||
let id = LINE_AUTO_INCREMENT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let srd = create_swapchain(xr, gfx, [1, 1, 1], SwapchainOpts::new())?;
|
||||
let srd = create_swapchain(xr, app.gfx.clone(), [1, 1, 1], SwapchainOpts::new())?;
|
||||
self.lines.insert(
|
||||
id,
|
||||
LineContainer {
|
||||
swapchain: srd,
|
||||
buf_color,
|
||||
pass,
|
||||
maybe_line: None,
|
||||
},
|
||||
);
|
||||
@@ -165,13 +162,13 @@ impl LinePool {
|
||||
.next()
|
||||
.unwrap();
|
||||
|
||||
self.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
||||
line.buf_color.write()?[0..6].copy_from_slice(&COLORS[inner.color]);
|
||||
|
||||
let mut cmd_buffer = app
|
||||
.gfx
|
||||
.create_gfx_command_buffer(CommandBufferUsage::OneTimeSubmit)?;
|
||||
cmd_buffer.begin_rendering(tgt, WGfxClearMode::DontCare)?;
|
||||
cmd_buffer.run_ref(&self.pass)?;
|
||||
cmd_buffer.run_ref(&line.pass)?;
|
||||
cmd_buffer.end_rendering()?;
|
||||
|
||||
futures.execute(cmd_buffer.queue.clone(), cmd_buffer.build()?)?;
|
||||
@@ -217,5 +214,7 @@ pub(super) struct Line {
|
||||
|
||||
struct LineContainer {
|
||||
swapchain: WlxSwapchain,
|
||||
buf_color: Subbuffer<[f32]>,
|
||||
pass: WGfxPass<Vert2Uv>,
|
||||
maybe_line: Option<Line>,
|
||||
}
|
||||
|
||||
@@ -149,8 +149,8 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
};
|
||||
|
||||
let pointer_lines = [
|
||||
lines.allocate(&xr_state, app.gfx.clone())?,
|
||||
lines.allocate(&xr_state, app.gfx.clone())?,
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
lines.allocate(&xr_state, &mut app)?,
|
||||
];
|
||||
|
||||
let watch_id = overlays.lookup(WATCH_NAME).unwrap(); // want panic
|
||||
@@ -238,6 +238,7 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
|
||||
if !session_running {
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
log::trace!("session not running: continue");
|
||||
continue 'main_loop;
|
||||
}
|
||||
|
||||
@@ -387,10 +388,12 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
for o in overlays.values_mut() {
|
||||
o.data.cur_visible = false;
|
||||
let Some(alpha) = o.config.active_state.as_ref().map(|x| x.alpha) else {
|
||||
log::trace!("{}: hidden, skip render", o.config.name);
|
||||
continue;
|
||||
};
|
||||
|
||||
if !o.data.init {
|
||||
log::trace!("{}: init", o.config.name);
|
||||
o.init(&mut app)?;
|
||||
o.data.init = true;
|
||||
}
|
||||
@@ -400,8 +403,10 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
ShouldRender::Can => (o.data.last_alpha - alpha).abs() > f32::EPSILON,
|
||||
ShouldRender::Unable => false, //try show old image if exists
|
||||
};
|
||||
log::trace!("{}: should_render returned: {should_render}", o.config.name);
|
||||
|
||||
if should_render {
|
||||
log::trace!("{}: render new frame", o.config.name);
|
||||
let meta = o.config.backend.frame_meta().unwrap(); // want panic
|
||||
let wsi = o.ensure_swapchain_acquire(&app, &xr_state, meta.extent)?;
|
||||
let tgt = RenderTarget { views: wsi.views };
|
||||
@@ -410,7 +415,10 @@ pub fn openxr_run(show_by_default: bool, headless: bool) -> Result<(), BackendEr
|
||||
o.data.last_alpha = alpha;
|
||||
futures.execute_results(rdr.end()?)?;
|
||||
} else if o.data.swapchain.is_none() {
|
||||
log::trace!("{}: not showing due to missing swapchain", o.config.name);
|
||||
continue;
|
||||
} else {
|
||||
log::trace!("{}: showing stale frame", o.config.name);
|
||||
}
|
||||
o.data.cur_visible = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use glam::{Affine3A, Quat, Vec3A};
|
||||
use glam::{Affine3A, Quat, Vec3A, vec3a};
|
||||
use libmonado::{Monado, Pose, ReferenceSpaceType};
|
||||
|
||||
use crate::{
|
||||
@@ -60,10 +60,10 @@ impl PlayspaceMover {
|
||||
pub fn update(
|
||||
&mut self,
|
||||
overlays: &mut OverlayWindowManager<OpenXrOverlayData>,
|
||||
state: &AppState,
|
||||
app: &AppState,
|
||||
monado: &mut Monado,
|
||||
) {
|
||||
for pointer in &state.input_state.pointers {
|
||||
for pointer in &app.input_state.pointers {
|
||||
if pointer.now.space_reset {
|
||||
if !pointer.before.space_reset {
|
||||
log::info!("Space reset");
|
||||
@@ -74,7 +74,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(mut data) = self.rotate.take() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_rotate {
|
||||
self.last_transform = data.pose;
|
||||
log::info!("End space rotate");
|
||||
@@ -82,10 +82,10 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
let new_hand =
|
||||
Quat::from_affine3(&(data.pose * state.input_state.pointers[data.hand].raw_pose));
|
||||
Quat::from_affine3(&(data.pose * app.input_state.pointers[data.hand].raw_pose));
|
||||
|
||||
let dq = new_hand * data.hand_pose.conjugate();
|
||||
let mut space_transform = if state.session.config.space_rotate_unlocked {
|
||||
let mut space_transform = if app.session.config.space_rotate_unlocked {
|
||||
Affine3A::from_quat(dq)
|
||||
} else {
|
||||
let rel_y = f32::atan2(
|
||||
@@ -95,8 +95,8 @@ impl PlayspaceMover {
|
||||
|
||||
Affine3A::from_rotation_y(rel_y)
|
||||
};
|
||||
let offset = (space_transform.transform_vector3a(state.input_state.hmd.translation)
|
||||
- state.input_state.hmd.translation)
|
||||
let offset = (space_transform.transform_vector3a(app.input_state.hmd.translation)
|
||||
- app.input_state.hmd.translation)
|
||||
* -1.0;
|
||||
|
||||
space_transform.translation = offset;
|
||||
@@ -107,7 +107,7 @@ impl PlayspaceMover {
|
||||
apply_offset(data.pose, monado);
|
||||
self.rotate = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_rotate {
|
||||
let hand_pose = Quat::from_affine3(&(self.last_transform * pointer.raw_pose));
|
||||
self.rotate = Some(MoverData {
|
||||
@@ -123,7 +123,7 @@ impl PlayspaceMover {
|
||||
}
|
||||
|
||||
if let Some(mut data) = self.drag.take() {
|
||||
let pointer = &state.input_state.pointers[data.hand];
|
||||
let pointer = &app.input_state.pointers[data.hand];
|
||||
if !pointer.now.space_drag {
|
||||
self.last_transform = data.pose;
|
||||
log::info!("End space drag");
|
||||
@@ -132,9 +132,13 @@ impl PlayspaceMover {
|
||||
|
||||
let new_hand = data
|
||||
.pose
|
||||
.transform_point3a(state.input_state.pointers[data.hand].raw_pose.translation);
|
||||
let relative_pos =
|
||||
(new_hand - data.hand_pose) * state.session.config.space_drag_multiplier;
|
||||
.transform_point3a(app.input_state.pointers[data.hand].raw_pose.translation);
|
||||
|
||||
let relative_pos = if app.session.config.space_drag_unlocked {
|
||||
new_hand - data.hand_pose
|
||||
} else {
|
||||
vec3a(0., new_hand.y - data.hand_pose.y, 0.)
|
||||
} * app.session.config.space_drag_multiplier;
|
||||
|
||||
if relative_pos.length_squared() > 1000.0 {
|
||||
log::warn!("Space drag too fast, ignoring");
|
||||
@@ -159,7 +163,7 @@ impl PlayspaceMover {
|
||||
apply_offset(data.pose, monado);
|
||||
self.drag = Some(data);
|
||||
} else {
|
||||
for (i, pointer) in state.input_state.pointers.iter().enumerate() {
|
||||
for (i, pointer) in app.input_state.pointers.iter().enumerate() {
|
||||
if pointer.now.space_drag {
|
||||
let hand_pos = self
|
||||
.last_transform
|
||||
@@ -234,7 +238,7 @@ impl PlayspaceMover {
|
||||
|
||||
let y1 = input.pointers[0].raw_pose.translation.y;
|
||||
let y2 = input.pointers[1].raw_pose.translation.y;
|
||||
let delta = y1.min(y2) - 0.03;
|
||||
let delta = y1.min(y2) - 0.05;
|
||||
|
||||
pose.position.y += delta;
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ pub fn dds_to_vk(dds_fmt: ImageFormat) -> anyhow::Result<Format> {
|
||||
ImageFormat::Rgba8UnormSrgb => Ok(Format::R8G8B8A8_SRGB),
|
||||
ImageFormat::Rgba16Float => Ok(Format::R16G16B16A16_SFLOAT),
|
||||
ImageFormat::Rgba32Float => Ok(Format::R32G32B32A32_SFLOAT),
|
||||
ImageFormat::Bgr8Unorm => Ok(Format::B8G8R8_UNORM),
|
||||
ImageFormat::Bgra8Unorm => Ok(Format::B8G8R8A8_UNORM),
|
||||
ImageFormat::Bgra8UnormSrgb => Ok(Format::B8G8R8A8_SRGB),
|
||||
// DXT1
|
||||
|
||||
@@ -81,6 +81,17 @@ Supported events:
|
||||
<button _press="..." _release="..." />
|
||||
```
|
||||
|
||||
Laser-color-specific variants are also available
|
||||
- `_press_left` & `_release_left` for blue laser
|
||||
- `_press_right` & `_release_right` for orange laser
|
||||
- `_press_middle` & `_release_middle` for purple laser
|
||||
|
||||
Release after short/long press (length controlled by config `long_press_duration`)
|
||||
- `_short_release` & `_long_release` for any laser
|
||||
- `_short_release_left` & `_long_release_left` for blue laser
|
||||
- `_short_release_right` & `_long_release_right` for orange laser
|
||||
- `_short_release_middle` & `_long_release_middle` for purple laser
|
||||
|
||||
#### Supported button actions
|
||||
|
||||
##### `::ShellExec <command> [args ..]`
|
||||
|
||||
@@ -10,7 +10,9 @@ use std::{
|
||||
use anyhow::Context;
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
event::{self, EventCallback, EventListenerKind},
|
||||
event::{
|
||||
self, CallbackData, CallbackMetadata, EventCallback, EventListenerKind, MouseButtonIndex,
|
||||
},
|
||||
i18n::Translation,
|
||||
layout::Layout,
|
||||
parser::CustomAttribsInfoOwned,
|
||||
@@ -31,18 +33,159 @@ use crate::{
|
||||
#[cfg(feature = "wayvr")]
|
||||
use crate::backend::wayvr::WayVRAction;
|
||||
|
||||
pub const BUTTON_EVENTS: [(&str, EventListenerKind); 2] = [
|
||||
("_press", EventListenerKind::MousePress),
|
||||
("_release", EventListenerKind::MouseRelease),
|
||||
pub const BUTTON_EVENTS: [(
|
||||
&str,
|
||||
EventListenerKind,
|
||||
fn(&mut CallbackData) -> bool,
|
||||
fn(&ComponentButton, &AppState) -> bool,
|
||||
); 16] = [
|
||||
(
|
||||
"_press",
|
||||
EventListenerKind::MousePress,
|
||||
button_any,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_left",
|
||||
EventListenerKind::MousePress,
|
||||
button_left,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_right",
|
||||
EventListenerKind::MousePress,
|
||||
button_right,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_press_middle",
|
||||
EventListenerKind::MousePress,
|
||||
button_middle,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
ignore_duration,
|
||||
),
|
||||
(
|
||||
"_short_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_short_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
short_duration,
|
||||
),
|
||||
(
|
||||
"_long_release",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_any,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_left",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_left,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_right",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_right,
|
||||
long_duration,
|
||||
),
|
||||
(
|
||||
"_long_release_middle",
|
||||
EventListenerKind::MouseRelease,
|
||||
button_middle,
|
||||
long_duration,
|
||||
),
|
||||
];
|
||||
|
||||
fn button_any(_: &mut CallbackData) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn button_left(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Left = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn button_right(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Right = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn button_middle(data: &mut CallbackData) -> bool {
|
||||
if let CallbackMetadata::MouseButton(b) = data.metadata
|
||||
&& let MouseButtonIndex::Middle = b.index
|
||||
{
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn ignore_duration(_btn: &ComponentButton, _app: &AppState) -> bool {
|
||||
true
|
||||
}
|
||||
fn long_duration(btn: &ComponentButton, app: &AppState) -> bool {
|
||||
btn.get_time_since_last_pressed().as_secs_f32() > app.session.config.long_press_duration
|
||||
}
|
||||
fn short_duration(btn: &ComponentButton, app: &AppState) -> bool {
|
||||
btn.get_time_since_last_pressed().as_secs_f32() < app.session.config.long_press_duration
|
||||
}
|
||||
|
||||
pub(super) fn setup_custom_button<S: 'static>(
|
||||
layout: &mut Layout,
|
||||
attribs: &CustomAttribsInfoOwned,
|
||||
_app: &AppState,
|
||||
button: Rc<ComponentButton>,
|
||||
) {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -52,9 +195,15 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, S> = match command {
|
||||
#[cfg(feature = "wayvr")]
|
||||
"::DashToggle" => Box::new(move |_common, _data, app, _| {
|
||||
"::DashToggle" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::WayVR(WayVRAction::ToggleDashboard));
|
||||
Ok(EventResult::Consumed)
|
||||
@@ -65,7 +214,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleSet(set_idx)));
|
||||
Ok(EventResult::Consumed)
|
||||
@@ -77,7 +230,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
return;
|
||||
};
|
||||
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Name(arg.clone()),
|
||||
Box::new(move |app, owc| {
|
||||
@@ -91,54 +248,84 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditToggle" => Box::new(move |_common, _data, app, _| {
|
||||
"::EditToggle" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::ToggleEditMode));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
#[cfg(feature = "wayland")]
|
||||
"::NewMirror" => Box::new(move |_common, _data, app, _| {
|
||||
let name = crate::overlays::mirror::new_mirror_name();
|
||||
"::NewMirror" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let name = crate::overlays::screen::mirror::new_mirror_name();
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Create(
|
||||
OverlaySelector::Name(name.clone()),
|
||||
Box::new(move |app| {
|
||||
Some(crate::overlays::mirror::new_mirror(name, &app.session))
|
||||
Some(crate::overlays::screen::mirror::new_mirror(
|
||||
name,
|
||||
&app.session,
|
||||
))
|
||||
}),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::CleanupMirrors" => Box::new(move |_common, _data, app, _| {
|
||||
"::CleanupMirrors" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::CleanupMirrors));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceReset" => Box::new(move |_common, _data, app, _| {
|
||||
"::PlayspaceReset" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Playspace(PlayspaceTask::Reset));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceRecenter" => Box::new(move |_common, _data, app, _| {
|
||||
"::PlayspaceRecenter" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Playspace(PlayspaceTask::Recenter));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::PlayspaceFixFloor" => Box::new(move |_common, _data, app, _| {
|
||||
for i in 0..5 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
format!("Fixing floor in {}", 5 - i),
|
||||
"Touch your controller to the floor!".into(),
|
||||
)
|
||||
.with_timeout(1.)
|
||||
.with_sound(true)
|
||||
.submit_at(app, Instant::now() + Duration::from_secs(i));
|
||||
"::PlayspaceFixFloor" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"TOAST.FIXING_FLOOR".into(),
|
||||
"TOAST.ONE_CONTROLLER_ON_FLOOR".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
.submit(app);
|
||||
|
||||
app.tasks.enqueue_at(
|
||||
TaskType::Playspace(PlayspaceTask::FixFloor),
|
||||
Instant::now() + Duration::from_secs(5),
|
||||
);
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::Shutdown" => Box::new(move |_common, _data, _app, _| {
|
||||
"::Shutdown" => Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
RUNNING.store(false, Ordering::Relaxed);
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
@@ -155,7 +342,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
log::error!("{command} has bad/missing arguments");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.hid_provider.send_key_routed(key, down);
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
@@ -182,7 +373,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
}),
|
||||
);
|
||||
|
||||
Box::new(move |_common, _data, _app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let _ = shell_on_action(&state).inspect_err(|e| log::error!("{e:?}"));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
@@ -206,7 +401,11 @@ pub(super) fn setup_custom_button<S: 'static>(
|
||||
osc_args.push(osc_arg);
|
||||
}
|
||||
|
||||
Box::new(move |_common, _data, app, _| {
|
||||
Box::new(move |_common, data, app, _| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(sender) = app.osc_sender.as_mut() else {
|
||||
log::error!("OscSend: sender is not available.");
|
||||
return Ok(EventResult::Consumed);
|
||||
|
||||
@@ -70,7 +70,8 @@ pub type OnCustomIdFunc<S> = Box<
|
||||
) -> anyhow::Result<()>,
|
||||
>;
|
||||
|
||||
pub type OnCustomAttribFunc = Box<dyn Fn(&mut Layout, &CustomAttribsInfoOwned, &AppState)>;
|
||||
pub type OnCustomAttribFunc =
|
||||
Box<dyn Fn(&mut Layout, &ParserState, &CustomAttribsInfoOwned, &AppState)>;
|
||||
|
||||
pub struct NewGuiPanelParams<S> {
|
||||
pub on_custom_id: Option<OnCustomIdFunc<S>>, // used only in `new_from_template`
|
||||
@@ -155,7 +156,7 @@ impl<S: 'static> GuiPanel<S> {
|
||||
}
|
||||
|
||||
if let Some(on_custom_attrib) = ¶ms.on_custom_attrib {
|
||||
on_custom_attrib(&mut layout, elem, app);
|
||||
on_custom_attrib(&mut layout, &parser_state, elem, app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Context;
|
||||
use glam::FloatExt;
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::button::ComponentButton,
|
||||
event::CallbackDataCommon,
|
||||
layout::WidgetID,
|
||||
parser::Fetchable,
|
||||
@@ -15,6 +18,7 @@ pub(super) struct InteractLockHandler {
|
||||
id: WidgetID,
|
||||
color: wgui::drawing::Color,
|
||||
interactable: bool,
|
||||
button: Option<Rc<ComponentButton>>,
|
||||
}
|
||||
|
||||
impl InteractLockHandler {
|
||||
@@ -27,10 +31,13 @@ impl InteractLockHandler {
|
||||
.get_as::<WidgetRectangle>(id)
|
||||
.context("Element with id=\"shadow\" must be a <rectangle>")?;
|
||||
|
||||
let button = panel.parser_state.fetch_component_as("top_lock")?;
|
||||
|
||||
Ok(Self {
|
||||
id,
|
||||
color: shadow_rect.params.color,
|
||||
interactable: true,
|
||||
button: Some(button),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -42,6 +49,10 @@ impl InteractLockHandler {
|
||||
.get_as::<WidgetRectangle>(self.id)
|
||||
.unwrap(); // can only fail if set_up_rect has issues
|
||||
|
||||
if let Some(button) = self.button.as_ref() {
|
||||
button.set_sticky_state(common, !interactable);
|
||||
}
|
||||
|
||||
let globals = common.state.globals.get();
|
||||
if interactable {
|
||||
set_anim_color(&mut rect, 0.0, self.color, globals.defaults.danger_color);
|
||||
@@ -54,16 +65,20 @@ impl InteractLockHandler {
|
||||
&mut self,
|
||||
common: &mut CallbackDataCommon,
|
||||
app: &mut AppState,
|
||||
anim_mult: f32,
|
||||
) -> Box<ModifyOverlayTask> {
|
||||
let defaults = app.wgui_globals.get().defaults.clone();
|
||||
let rect_color = self.color;
|
||||
|
||||
self.interactable = !self.interactable;
|
||||
if let Some(button) = self.button.as_ref() {
|
||||
button.set_sticky_state(common, !self.interactable);
|
||||
}
|
||||
|
||||
let anim = if self.interactable {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
@@ -79,7 +94,7 @@ impl InteractLockHandler {
|
||||
} else {
|
||||
Animation::new(
|
||||
self.id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
|
||||
@@ -47,21 +47,9 @@ mod sprite_tab;
|
||||
mod stereo;
|
||||
pub mod tab;
|
||||
|
||||
pub(super) struct LongPressButtonState {
|
||||
pub(super) pressed: Instant,
|
||||
}
|
||||
impl Default for LongPressButtonState {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
pressed: Instant::now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct EditModeState {
|
||||
tasks: Rc<RefCell<TaskContainer>>,
|
||||
id: Rc<RefCell<OverlayID>>,
|
||||
delete: LongPressButtonState,
|
||||
tabs: ButtonPaneTabSwitcher,
|
||||
lock: InteractLockHandler,
|
||||
pos: SpriteTabHandler<PosTabState>,
|
||||
@@ -272,9 +260,6 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
let state = EditModeState {
|
||||
id: Rc::new(RefCell::new(OverlayID::null())),
|
||||
tasks: Rc::new(RefCell::new(TaskContainer::new())),
|
||||
delete: LongPressButtonState {
|
||||
pressed: Instant::now(),
|
||||
},
|
||||
tabs: ButtonPaneTabSwitcher::default(),
|
||||
lock: InteractLockHandler::default(),
|
||||
pos: SpriteTabHandler::default(),
|
||||
@@ -282,8 +267,16 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
mouse: SpriteTabHandler::default(),
|
||||
};
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let anim_mult = app.wgui_globals.defaults().animation_mult;
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
|
||||
let Ok(button) =
|
||||
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -293,15 +286,25 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, EditModeState> = match command {
|
||||
"::EditModeToggleLock" => Box::new(move |common, _data, app, state| {
|
||||
"::EditModeToggleLock" => Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.lock.toggle(common, app);
|
||||
let task = state.lock.toggle(common, app, anim_mult);
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::Modify(sel, task)));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeToggleGrab" => Box::new(move |_common, _data, app, state| {
|
||||
"::EditModeToggleGrab" => Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
sel,
|
||||
@@ -314,14 +317,22 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}),
|
||||
"::EditModeTab" => {
|
||||
let tab_name = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, _app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
state.tabs.tab_button_clicked(common, &tab_name);
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeSetPos" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.pos.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -331,7 +342,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}
|
||||
"::EditModeSetStereo" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.stereo.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -341,7 +356,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
}
|
||||
"::EditModeSetMouse" => {
|
||||
let key = args.next().unwrap().to_owned();
|
||||
Box::new(move |common, _data, app, state| {
|
||||
Box::new(move |common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let sel = OverlaySelector::Id(*state.id.borrow());
|
||||
let task = state.mouse.button_clicked(common, &key);
|
||||
app.tasks
|
||||
@@ -349,15 +368,11 @@ fn make_edit_panel(app: &mut AppState) -> anyhow::Result<EditModeWrapPanel> {
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::EditModeDeletePress" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
// TODO: animate to light up button after 2s
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteRelease" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
|
||||
"::EditModeDelete" => Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Id(*state.id.borrow()),
|
||||
Box::new(move |_app, owc| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::{gui::panel::GuiPanel, state::AppState, subsystem::hid::XkbKeymap};
|
||||
use glam::{FloatExt, Mat4, Vec2, Vec3, vec2};
|
||||
use glam::{FloatExt, Mat4, Vec2, vec2, vec3};
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
assets::AssetPath,
|
||||
@@ -39,11 +39,13 @@ pub(super) fn create_keyboard_panel(
|
||||
let globals = app.wgui_globals.clone();
|
||||
let accent_color = globals.get().defaults.accent_color;
|
||||
|
||||
let anim_mult = globals.defaults().animation_mult;
|
||||
|
||||
let (background, _) = panel.layout.add_child(
|
||||
panel.layout.content_root_widget,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: wgui::drawing::Color::new(0., 0., 0., 0.75),
|
||||
round: WLength::Units(16.0),
|
||||
color: globals.defaults().bg_color,
|
||||
round: WLength::Units((16.0 * globals.defaults().rounding_mult).max(0.)),
|
||||
border: 2.0,
|
||||
border_color: accent_color,
|
||||
..Default::default()
|
||||
@@ -169,6 +171,8 @@ pub(super) fn create_keyboard_panel(
|
||||
})
|
||||
};
|
||||
|
||||
let width_mul = 1. / my_size_f32;
|
||||
|
||||
panel.add_event_listener(
|
||||
widget_id,
|
||||
EventListenerKind::MouseEnter,
|
||||
@@ -176,7 +180,14 @@ pub(super) fn create_keyboard_panel(
|
||||
let k = key_state.clone();
|
||||
move |common, data, _app, _state| {
|
||||
common.alterables.trigger_haptics();
|
||||
on_enter_anim(k.clone(), common, data, accent_color);
|
||||
on_enter_anim(
|
||||
k.clone(),
|
||||
common,
|
||||
data,
|
||||
accent_color,
|
||||
anim_mult,
|
||||
width_mul,
|
||||
);
|
||||
Ok(EventResult::Pass)
|
||||
}
|
||||
}),
|
||||
@@ -188,7 +199,14 @@ pub(super) fn create_keyboard_panel(
|
||||
let k = key_state.clone();
|
||||
move |common, data, _app, _state| {
|
||||
common.alterables.trigger_haptics();
|
||||
on_leave_anim(k.clone(), common, data, accent_color);
|
||||
on_leave_anim(
|
||||
k.clone(),
|
||||
common,
|
||||
data,
|
||||
accent_color,
|
||||
anim_mult,
|
||||
width_mul,
|
||||
);
|
||||
Ok(EventResult::Pass)
|
||||
}
|
||||
}),
|
||||
@@ -254,11 +272,14 @@ pub(super) fn create_keyboard_panel(
|
||||
|
||||
const BUTTON_HOVER_SCALE: f32 = 0.1;
|
||||
|
||||
fn get_anim_transform(pos: f32, widget_size: Vec2) -> Mat4 {
|
||||
util::centered_matrix(
|
||||
widget_size,
|
||||
&Mat4::from_scale(Vec3::splat(BUTTON_HOVER_SCALE.mul_add(pos, 1.0))),
|
||||
)
|
||||
fn get_anim_transform(pos: f32, widget_size: Vec2, width_mult: f32) -> Mat4 {
|
||||
let scale = vec3(
|
||||
(BUTTON_HOVER_SCALE * width_mult).mul_add(pos, 1.0),
|
||||
BUTTON_HOVER_SCALE.mul_add(pos, 1.0),
|
||||
1.0,
|
||||
);
|
||||
|
||||
util::centered_matrix(widget_size, &Mat4::from_scale(scale))
|
||||
}
|
||||
|
||||
fn set_anim_color(
|
||||
@@ -291,15 +312,18 @@ fn on_enter_anim(
|
||||
common: &mut event::CallbackDataCommon,
|
||||
data: &event::CallbackData,
|
||||
accent_color: drawing::Color,
|
||||
anim_mult: f32,
|
||||
width_mult: f32,
|
||||
) {
|
||||
common.alterables.animate(Animation::new(
|
||||
data.widget_id,
|
||||
10,
|
||||
(10. * anim_mult) as _,
|
||||
AnimationEasing::OutBack,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(&key_state, rect, data.pos, accent_color);
|
||||
data.data.transform = get_anim_transform(data.pos, data.widget_boundary.size);
|
||||
data.data.transform =
|
||||
get_anim_transform(data.pos, data.widget_boundary.size, width_mult);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
@@ -310,15 +334,18 @@ fn on_leave_anim(
|
||||
common: &mut event::CallbackDataCommon,
|
||||
data: &event::CallbackData,
|
||||
accent_color: drawing::Color,
|
||||
anim_mult: f32,
|
||||
width_mult: f32,
|
||||
) {
|
||||
common.alterables.animate(Animation::new(
|
||||
data.widget_id,
|
||||
15,
|
||||
(15. * anim_mult) as _,
|
||||
AnimationEasing::OutQuad,
|
||||
Box::new(move |common, data| {
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
set_anim_color(&key_state, rect, 1.0 - data.pos, accent_color);
|
||||
data.data.transform = get_anim_transform(1.0 - data.pos, data.widget_boundary.size);
|
||||
data.data.transform =
|
||||
get_anim_transform(1.0 - data.pos, data.widget_boundary.size, width_mult);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
|
||||
@@ -2,8 +2,6 @@ pub mod anchor;
|
||||
pub mod custom;
|
||||
pub mod edit;
|
||||
pub mod keyboard;
|
||||
#[cfg(feature = "wayland")]
|
||||
pub mod mirror;
|
||||
pub mod screen;
|
||||
pub mod toast;
|
||||
pub mod watch;
|
||||
|
||||
@@ -215,6 +215,7 @@ impl OverlayBackend for ScreenBackend {
|
||||
Ok(ShouldRender::Can)
|
||||
}
|
||||
} else {
|
||||
log::trace!("{}: backend ready, but no image received.", self.name);
|
||||
Ok(ShouldRender::Unable)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ use std::{
|
||||
|
||||
use futures::{Future, FutureExt};
|
||||
use glam::{Affine2, Affine3A, Quat, Vec3, vec3};
|
||||
use wlx_capture::pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen};
|
||||
use wlx_capture::{
|
||||
WlxCapture,
|
||||
pipewire::{PipewireCapture, PipewireSelectScreenResult, pipewire_select_screen},
|
||||
};
|
||||
use wlx_common::{
|
||||
overlays::{BackendAttrib, BackendAttribValue},
|
||||
windowing::OverlayWindowState,
|
||||
@@ -19,6 +22,7 @@ use crate::{
|
||||
input::{HoverResult, PointerHit},
|
||||
task::{OverlayTask, TaskType},
|
||||
},
|
||||
overlays::screen::capture::{MainThreadWlxCapture, new_wlx_capture},
|
||||
state::{AppSession, AppState},
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::{
|
||||
@@ -31,7 +35,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
use super::screen::backend::ScreenBackend;
|
||||
use super::backend::ScreenBackend;
|
||||
type PinnedSelectorFuture = core::pin::Pin<
|
||||
Box<dyn Future<Output = Result<PipewireSelectScreenResult, wlx_capture::pipewire::AshpdError>>>,
|
||||
>;
|
||||
@@ -76,13 +80,21 @@ impl OverlayBackend for MirrorBackend {
|
||||
|
||||
match maybe_pw_result {
|
||||
Ok(pw_result) => {
|
||||
log::debug!(
|
||||
"{}: PipeWire result streams: {:?}",
|
||||
self.name,
|
||||
&pw_result.streams
|
||||
);
|
||||
let node_id = pw_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
|
||||
log::info!("{}: PipeWire node selected: {}", self.name.clone(), node_id);
|
||||
let capture = PipewireCapture::new(self.name.clone(), node_id);
|
||||
log::info!("{}: PipeWire node selected: {}", self.name, node_id);
|
||||
let capture = new_wlx_capture!(
|
||||
app.gfx_extras.queue_capture,
|
||||
PipewireCapture::new(self.name.clone(), node_id)
|
||||
);
|
||||
self.renderer = Some(ScreenBackend::new_raw(
|
||||
self.name.clone(),
|
||||
app.xr_backend,
|
||||
Box::new(capture),
|
||||
capture,
|
||||
));
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Name(self.name.clone()),
|
||||
@@ -15,6 +15,8 @@ use crate::{
|
||||
|
||||
pub mod backend;
|
||||
mod capture;
|
||||
#[cfg(feature = "wayland")]
|
||||
pub mod mirror;
|
||||
#[cfg(feature = "pipewire")]
|
||||
pub mod pw;
|
||||
#[cfg(feature = "wayland")]
|
||||
|
||||
@@ -42,6 +42,12 @@ impl ScreenBackend {
|
||||
false,
|
||||
)?;
|
||||
|
||||
log::debug!(
|
||||
"{}: PipeWire result streams: {:?}",
|
||||
output.name,
|
||||
&select_screen_result.streams
|
||||
);
|
||||
|
||||
let node_id = select_screen_result.streams.first().unwrap().node_id; // streams guaranteed to have at least one element
|
||||
|
||||
let capture = new_wlx_capture!(
|
||||
|
||||
@@ -16,7 +16,6 @@ use wlx_common::{
|
||||
use crate::{
|
||||
backend::task::{OverlayTask, TaskType},
|
||||
gui::panel::{GuiPanel, NewGuiPanelParams, OnCustomIdFunc},
|
||||
overlays::watch::{WATCH_POS, WATCH_ROT},
|
||||
state::AppState,
|
||||
windowing::{OverlaySelector, Z_ORDER_TOAST, window::OverlayWindowConfig},
|
||||
};
|
||||
@@ -121,35 +120,29 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
Positioning::FollowHead { lerp: 0.1 },
|
||||
),
|
||||
ToastDisplayMethod::Watch => {
|
||||
//FIXME: properly follow watch
|
||||
let watch_pos = WATCH_POS + vec3(-0.005, -0.05, 0.02);
|
||||
let watch_rot = WATCH_ROT;
|
||||
let relative_to = /*match app.session.config.watch_hand {
|
||||
LeftRight::Left =>*/ Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
lerp: 1.0,
|
||||
align_to_hmd: true,
|
||||
/*
|
||||
},
|
||||
LeftRight::Right => {
|
||||
watch_pos.x = -watch_pos.x;
|
||||
watch_rot = watch_rot * Quat::from_rotation_x(PI) * Quat::from_rotation_z(PI);
|
||||
Positioning::FollowHand {
|
||||
hand: LeftRight::Right,
|
||||
lerp: 1.0,
|
||||
}
|
||||
}*/
|
||||
let relative_to = Positioning::FollowHand {
|
||||
hand: LeftRight::Left,
|
||||
lerp: 0.1,
|
||||
align_to_hmd: true,
|
||||
};
|
||||
(watch_pos, watch_rot, relative_to)
|
||||
(vec3(0., 0., 0.), Quat::IDENTITY, relative_to)
|
||||
}
|
||||
};
|
||||
|
||||
let title = if toast.title.is_empty() {
|
||||
Translation::from_translation_key("TOAST.DEFAULT_TITLE")
|
||||
} else if matches!(toast.topic, ToastTopic::System | ToastTopic::Error) {
|
||||
Translation::from_translation_key(&toast.title)
|
||||
} else {
|
||||
Translation::from_raw_text(&toast.title)
|
||||
};
|
||||
|
||||
let body = if matches!(toast.topic, ToastTopic::System) {
|
||||
Translation::from_translation_key(&toast.body)
|
||||
} else {
|
||||
Translation::from_raw_text(&toast.body)
|
||||
};
|
||||
|
||||
let on_custom_id: OnCustomIdFunc<()> =
|
||||
Box::new(move |id, widget, _doc_params, layout, _parser_state, ()| {
|
||||
if &*id == "toast_title" {
|
||||
@@ -168,7 +161,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
.get_as::<WidgetLabel>(widget)
|
||||
.context("toast.xml: missing element with id: toast_body")?;
|
||||
let mut globals = layout.state.globals.get();
|
||||
label.set_text_simple(&mut globals, Translation::from_raw_text(&toast.body));
|
||||
label.set_text_simple(&mut globals, body.clone());
|
||||
}
|
||||
Ok(())
|
||||
});
|
||||
@@ -206,7 +199,7 @@ fn new_toast(toast: Toast, app: &mut AppState) -> Option<OverlayWindowConfig> {
|
||||
}
|
||||
|
||||
fn msg_err(app: &mut AppState, message: &str) {
|
||||
Toast::new(ToastTopic::System, "Error".into(), message.into())
|
||||
Toast::new(ToastTopic::Error, "TOAST.ERROR".into(), message.into())
|
||||
.with_timeout(3.)
|
||||
.submit(app);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
rc::Rc,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{collections::HashMap, rc::Rc, time::Duration};
|
||||
|
||||
use glam::{Affine3A, Quat, Vec3, Vec3A, vec3};
|
||||
use idmap::{DirectIdMap, ordered::Keys};
|
||||
use idmap::DirectIdMap;
|
||||
use slotmap::SecondaryMap;
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
@@ -31,7 +27,6 @@ use crate::{
|
||||
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
|
||||
timer::GuiTimer,
|
||||
},
|
||||
overlays::edit::LongPressButtonState,
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlayID, OverlaySelector, Z_ORDER_WATCH,
|
||||
@@ -70,7 +65,6 @@ struct WatchState {
|
||||
keyboard_oid: OverlayID,
|
||||
dashboard_oid: OverlayID,
|
||||
num_sets: usize,
|
||||
delete: LongPressButtonState,
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_tightening)]
|
||||
@@ -78,8 +72,14 @@ struct WatchState {
|
||||
pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
let state = WatchState::default();
|
||||
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, attribs, _app| {
|
||||
for (name, kind) in &BUTTON_EVENTS {
|
||||
let on_custom_attrib: OnCustomAttribFunc = Box::new(move |layout, parser, attribs, _app| {
|
||||
let Ok(button) =
|
||||
parser.fetch_component_from_widget_id_as::<ComponentButton>(attribs.widget_id)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (name, kind, test_button, test_duration) in &BUTTON_EVENTS {
|
||||
let Some(action) = attribs.get_value(name) else {
|
||||
continue;
|
||||
};
|
||||
@@ -89,20 +89,23 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
continue;
|
||||
};
|
||||
|
||||
let button = button.clone();
|
||||
|
||||
let callback: EventCallback<AppState, WatchState> = match command {
|
||||
"::EditModeDeleteDown" => Box::new(move |_common, _data, _app, state| {
|
||||
state.delete.pressed = Instant::now();
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeDeleteUp" => Box::new(move |_common, _data, app, state| {
|
||||
if state.delete.pressed.elapsed() < Duration::from_secs(1) {
|
||||
return Ok(EventResult::Consumed);
|
||||
"::EditModeDeleteSet" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks
|
||||
.enqueue(TaskType::Overlay(OverlayTask::DeleteActiveSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
"::EditModeAddSet" => Box::new(move |_common, _data, app, _state| {
|
||||
"::EditModeAddSet" => Box::new(move |_common, data, app, _state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::AddSet));
|
||||
Ok(EventResult::Consumed)
|
||||
}),
|
||||
@@ -112,7 +115,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, state| {
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
@@ -137,7 +144,11 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, _data, app, state| {
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
@@ -150,6 +161,29 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
"::SingleSetOverlayReset" => {
|
||||
let arg = args.next().unwrap_or_default();
|
||||
let Ok(idx) = arg.parse::<usize>() else {
|
||||
log::error!("{command} has invalid argument: \"{arg}\"");
|
||||
return;
|
||||
};
|
||||
Box::new(move |_common, data, app, state| {
|
||||
if !test_button(data) || !test_duration(&button, app) {
|
||||
return Ok(EventResult::Pass);
|
||||
}
|
||||
|
||||
let Some(overlay) = state.overlay_metas.get(idx) else {
|
||||
log::error!("No overlay at index {idx}.");
|
||||
return Ok(EventResult::Consumed);
|
||||
};
|
||||
|
||||
app.tasks.enqueue(TaskType::Overlay(OverlayTask::Modify(
|
||||
OverlaySelector::Id(overlay.id),
|
||||
Box::new(|app, owc| owc.activate(app)),
|
||||
)));
|
||||
Ok(EventResult::Consumed)
|
||||
})
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
|
||||
@@ -28,8 +28,12 @@
|
||||
## How fast to drag when the space drag feature is activated
|
||||
#space_drag_multiplier: 1.0
|
||||
|
||||
## When enabled, the space_rotate binding can be used
|
||||
## to rotate in any axis. Imagine horizon mode².
|
||||
## Setting this to false will make space_drag
|
||||
## only affect the vertical axis (Y).
|
||||
#space_drag_unlocked: true
|
||||
|
||||
## Monado/WiVRn only. When enabled, the space_rotate binding
|
||||
## can rotate in any axis. Imagine horizon mode².
|
||||
#space_rotate_unlocked: false
|
||||
|
||||
## Monado/WiVRn only. Use passthrough camera if the headset supports it.
|
||||
@@ -87,9 +91,17 @@
|
||||
#theme_path: "theme"
|
||||
|
||||
## These can be used to control the color theme of WlxOverlay-S.
|
||||
#color_text: "#ffffff"
|
||||
#color_accent: "#008cff"
|
||||
#color_danger: "#ff3300"
|
||||
#color_faded: "#668299"
|
||||
#color_background: "#010206"
|
||||
|
||||
## Multiplier for animation speed. 2.0 → double speed, 0.5 → half speed
|
||||
#animation_speed: 1.0
|
||||
|
||||
## Adjust this between 0..1 for a more rectangular feel.
|
||||
#round_multiplier: 1.0
|
||||
|
||||
## Path to custom skybox texture, relative to `~/.config/wlxoverlay`
|
||||
#skybox_texture: ""
|
||||
@@ -163,6 +175,10 @@
|
||||
#xr_click_sensitivity: 0.7
|
||||
#xr_click_sensitivity_release: 0.5
|
||||
|
||||
## How many seconds to buttons need to be held
|
||||
## before it's considered a long press
|
||||
#long_press_duration: 1.0
|
||||
|
||||
## Change speed of scrolling
|
||||
# 0.5 → half the speed
|
||||
# 2.0 → twice the speed
|
||||
|
||||
@@ -3,7 +3,7 @@ use idmap::IdMap;
|
||||
use smallvec::{SmallVec, smallvec};
|
||||
use std::sync::Arc;
|
||||
use wgui::{
|
||||
font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||
drawing, font_config::WguiFontConfig, gfx::WGfx, globals::WguiGlobals, parser::parse_color_hex,
|
||||
renderer_vk::context::SharedContext as WSharedContext,
|
||||
};
|
||||
use wlx_common::{
|
||||
@@ -96,26 +96,21 @@ impl AppState {
|
||||
let theme = session.config.theme_path.clone();
|
||||
|
||||
let mut defaults = wgui::globals::Defaults::default();
|
||||
defaults.accent_color = session
|
||||
.config
|
||||
.color_accent
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.accent_color);
|
||||
|
||||
defaults.danger_color = session
|
||||
.config
|
||||
.color_danger
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.danger_color);
|
||||
fn apply_color(default: &mut drawing::Color, value: &Option<String>) {
|
||||
if let Some(parsed) = value.as_ref().and_then(|c| parse_color_hex(c)) {
|
||||
*default = parsed;
|
||||
}
|
||||
}
|
||||
|
||||
defaults.faded_color = session
|
||||
.config
|
||||
.color_faded
|
||||
.as_ref()
|
||||
.and_then(|c| parse_color_hex(&c))
|
||||
.unwrap_or(defaults.faded_color);
|
||||
apply_color(&mut defaults.text_color, &session.config.color_text);
|
||||
apply_color(&mut defaults.accent_color, &session.config.color_accent);
|
||||
apply_color(&mut defaults.danger_color, &session.config.color_danger);
|
||||
apply_color(&mut defaults.faded_color, &session.config.color_faded);
|
||||
apply_color(&mut defaults.bg_color, &session.config.color_background);
|
||||
|
||||
defaults.animation_mult = 1. / session.config.animation_speed;
|
||||
defaults.rounding_mult = session.config.round_multiplier;
|
||||
|
||||
let dbus = DbusConnector::default();
|
||||
|
||||
@@ -205,6 +200,7 @@ impl AppSession {
|
||||
|
||||
let mut toast_topics = IdMap::new();
|
||||
toast_topics.insert(ToastTopic::System, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::Error, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::DesktopNotification, ToastDisplayMethod::Center);
|
||||
toast_topics.insert(ToastTopic::XSNotification, ToastDisplayMethod::Center);
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ pub struct FrameMeta {
|
||||
pub clear: WGfxClearMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ShouldRender {
|
||||
/// The overlay is dirty and needs to be rendered.
|
||||
Should,
|
||||
|
||||
@@ -34,7 +34,7 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub const MAX_OVERLAY_SETS: usize = 7;
|
||||
pub const MAX_OVERLAY_SETS: usize = 6;
|
||||
|
||||
pub struct OverlayWindowManager<T> {
|
||||
wrappers: EditWrapperManager,
|
||||
@@ -49,6 +49,7 @@ pub struct OverlayWindowManager<T> {
|
||||
watch_id: OverlayID,
|
||||
edit_mode: bool,
|
||||
dropped_overlays: VecDeque<OverlayWindowData<T>>,
|
||||
initialized: bool,
|
||||
}
|
||||
|
||||
impl<T> OverlayWindowManager<T>
|
||||
@@ -66,6 +67,7 @@ where
|
||||
watch_id: OverlayID::null(), // set down below
|
||||
edit_mode: false,
|
||||
dropped_overlays: VecDeque::with_capacity(8),
|
||||
initialized: false,
|
||||
};
|
||||
|
||||
let mut wayland = false;
|
||||
@@ -148,6 +150,8 @@ where
|
||||
.notify(app, ev)?;
|
||||
}
|
||||
|
||||
me.initialized = true;
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
@@ -199,8 +203,8 @@ where
|
||||
let Some(set) = self.current_set else {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"No set is selected!".into(),
|
||||
"TOAST.CANNOT_REMOVE_SET".into(),
|
||||
"TOAST.NO_SET_SELECTED".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
@@ -211,8 +215,8 @@ where
|
||||
if self.sets.len() <= 1 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"Can't remove set".into(),
|
||||
"This is the last existing set!".into(),
|
||||
"TOAST.CANNOT_REMOVE_SET".into(),
|
||||
"TOAST.LAST_EXISTING_SET".into(),
|
||||
)
|
||||
.with_timeout(5.)
|
||||
.with_sound(true)
|
||||
@@ -658,6 +662,7 @@ impl<T> OverlayWindowManager<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut num_overlays = 0;
|
||||
let ws = &mut self.sets[new_set];
|
||||
for (id, data) in self.overlays.iter_mut().filter(|(_, d)| !d.config.global) {
|
||||
if let Some(state) = ws.overlays.remove(id) {
|
||||
@@ -666,10 +671,28 @@ impl<T> OverlayWindowManager<T> {
|
||||
if !keep_transforms {
|
||||
data.config.reset(app, false);
|
||||
}
|
||||
if !matches!(
|
||||
data.config.category,
|
||||
OverlayCategory::Internal
|
||||
| OverlayCategory::Keyboard
|
||||
| OverlayCategory::Dashboard
|
||||
) {
|
||||
num_overlays += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
ws.overlays.clear();
|
||||
self.restore_set = new_set;
|
||||
|
||||
if !self.edit_mode && self.initialized && num_overlays < 1 {
|
||||
Toast::new(
|
||||
ToastTopic::System,
|
||||
"TOAST.EMPTY_SET".into(),
|
||||
"TOAST.LETS_ADD_OVERLAYS".into(),
|
||||
)
|
||||
.with_timeout(3.)
|
||||
.submit(app);
|
||||
}
|
||||
}
|
||||
self.current_set = new_set;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user