Compare commits
10 commits
531114e841
...
7bddebb391
| Author | SHA1 | Date | |
|---|---|---|---|
| 7bddebb391 | |||
| ca7bc8439b | |||
| 282845b7e0 | |||
| 318b480c0e | |||
| 1f6e881880 | |||
| 26d831c891 | |||
| c17a21830e | |||
| debf970c36 | |||
| 757849940c | |||
| 9e7afd661e |
7 changed files with 18 additions and 147 deletions
|
|
@ -1,92 +0,0 @@
|
||||||
//! The opt-in switch for makepad's in-app developer overlays.
|
|
||||||
//!
|
|
||||||
//! Three of them exist, and each one binds a bare function key and then claims
|
|
||||||
//! input the app never sees:
|
|
||||||
//!
|
|
||||||
//! * **F10** — the exploded draw-list view ([`crate::sploded`]). Intercepted in
|
|
||||||
//! `Cx::call_event_handler` *before* the app's handler, and once it is up it
|
|
||||||
//! also claims Escape, the arrow keys, `+`/`-`/`0`, `I` and `H` — no modifier
|
|
||||||
//! required — plus every pointer drag outside the declared flat band.
|
|
||||||
//! * **F12** — the design tweaker (`makepad_widgets::tweaker`), a child of every
|
|
||||||
//! `Window`. Once it is up it swallows every pointer event over the body.
|
|
||||||
//! * **Shift+F12** — the screen recorder (`makepad_widgets::screen_cap`), which
|
|
||||||
//! writes mp4 files next to the running process.
|
|
||||||
//!
|
|
||||||
//! These are development tools, so they stay off unless a developer asks for
|
|
||||||
//! them. A shipped app is not a place to discover that a stray F10 tilts the
|
|
||||||
//! whole UI into 3D and stops Escape from closing anything.
|
|
||||||
//!
|
|
||||||
//! Turn them on with `--devtools` on the command line, or `MAKEPAD_DEVTOOLS=1`
|
|
||||||
//! in the environment. `--remote` implies them: the remote control surface's
|
|
||||||
//! `/snap` + `/click` loop drives the tweaker, so a remote-driven app has
|
|
||||||
//! already opted in to being instrumented. An explicit `MAKEPAD_DEVTOOLS=0`
|
|
||||||
//! wins over all of it, which is also how the off path stays testable under
|
|
||||||
//! `--remote`.
|
|
||||||
//!
|
|
||||||
//! Only the *hotkeys* are gated, not the tools. An app that wants one of these
|
|
||||||
//! on its own terms still calls `Cx::sploded_toggle`, `tweaker::set_tweak_on`
|
|
||||||
//! or `ScreenCap::toggle` directly — that is the app deciding, rather than a
|
|
||||||
//! key nobody knew was bound.
|
|
||||||
|
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
/// Whether this process opted into the developer overlays.
|
|
||||||
///
|
|
||||||
/// Scans argv and the environment once and caches the answer, so the hot event
|
|
||||||
/// path pays an atomic load. See the module docs for what this gates.
|
|
||||||
pub fn enabled() -> bool {
|
|
||||||
static ENABLED: OnceLock<bool> = OnceLock::new();
|
|
||||||
*ENABLED.get_or_init(|| {
|
|
||||||
decide(
|
|
||||||
std::env::args().any(|a| a == "--devtools"),
|
|
||||||
std::env::var("MAKEPAD_DEVTOOLS").ok().as_deref(),
|
|
||||||
crate::remote::requested(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The whole decision, with the process pulled out so it can be tested.
|
|
||||||
///
|
|
||||||
/// `MAKEPAD_DEVTOOLS` takes the usual off-ish spellings, so it can sit in a
|
|
||||||
/// shell profile as `0` instead of having to be unset — and because it is the
|
|
||||||
/// one explicit signal, an off spelling also overrides `--devtools` and
|
|
||||||
/// `--remote`.
|
|
||||||
fn decide(flag: bool, env: Option<&str>, remote: bool) -> bool {
|
|
||||||
if let Some(env) = env {
|
|
||||||
let env = env.trim().to_ascii_lowercase();
|
|
||||||
return !matches!(env.as_str(), "" | "0" | "off" | "false" | "no");
|
|
||||||
}
|
|
||||||
flag || remote
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::decide;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn off_by_default() {
|
|
||||||
assert!(!decide(false, None, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn the_flag_or_remote_turns_it_on() {
|
|
||||||
assert!(decide(true, None, false));
|
|
||||||
assert!(decide(false, None, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn the_env_var_turns_it_on() {
|
|
||||||
for on in ["1", "yes", "true", "on", " 1 "] {
|
|
||||||
assert!(decide(false, Some(on), false), "{on:?} should enable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn an_off_spelling_wins_over_the_flag_and_remote() {
|
|
||||||
// So `MAKEPAD_DEVTOOLS=0` can live in a profile, and so the gated-off
|
|
||||||
// path is still reachable under --remote.
|
|
||||||
for off in ["0", "off", "false", "no", "", " ", "OFF"] {
|
|
||||||
assert!(!decide(true, Some(off), true), "{off:?} should disable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -78,7 +78,6 @@ pub mod display_context;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod app_main;
|
mod app_main;
|
||||||
pub mod remote;
|
pub mod remote;
|
||||||
pub mod devtools;
|
|
||||||
pub mod pixel_probe;
|
pub mod pixel_probe;
|
||||||
pub mod screen_capture;
|
pub mod screen_capture;
|
||||||
pub mod audio_output_tap;
|
pub mod audio_output_tap;
|
||||||
|
|
|
||||||
|
|
@ -46,14 +46,11 @@ mod imp {
|
||||||
|
|
||||||
static ACTIVE: AtomicBool = AtomicBool::new(false);
|
static ACTIVE: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
/// True when this process asked for the remote bridge, in any of the forms
|
/// True when this process was started with `--remote` (any form). Pure
|
||||||
/// [`requested_bind`] accepts — including `MAKEPAD_REMOTE`, which a plain
|
/// argv scan, usable before the bridge itself is up — the platform's
|
||||||
/// argv scan used to miss, so `MAKEPAD_REMOTE=1` started the bridge while
|
/// focus policy reads it while the first window is being created.
|
||||||
/// everything keyed off this said no. Pure argv + env, usable before the
|
|
||||||
/// bridge itself is up: the platform's focus policy reads it while the
|
|
||||||
/// first window is being created.
|
|
||||||
pub fn requested() -> bool {
|
pub fn requested() -> bool {
|
||||||
requested_bind().is_some()
|
std::env::args().any(|a| a == "--remote" || a.starts_with("--remote="))
|
||||||
}
|
}
|
||||||
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
|
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
static LIVE_CONNS: AtomicUsize = AtomicUsize::new(0);
|
static LIVE_CONNS: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
@ -461,14 +458,9 @@ mod imp {
|
||||||
/// "the user dismissed this" apart from "the app crashed", and remember it
|
/// "the user dismissed this" apart from "the app crashed", and remember it
|
||||||
/// so requests aimed at that window get the real reason.
|
/// so requests aimed at that window get the real reason.
|
||||||
pub fn note_user_closed_window(window_id: usize, title: &str) {
|
pub fn note_user_closed_window(window_id: usize, title: &str) {
|
||||||
// Only chatter when the bridge is actually up: this line is for the
|
|
||||||
// agent driving the app, and a shipped app should not print
|
|
||||||
// `[makepad-remote] ...` to stdout every time a window closes.
|
|
||||||
let line = format!("[makepad-remote] user closed window {window_id} ({title:?})");
|
let line = format!("[makepad-remote] user closed window {window_id} ({title:?})");
|
||||||
if is_active() {
|
|
||||||
println!("{line}");
|
println!("{line}");
|
||||||
let _ = std::io::stdout().flush();
|
let _ = std::io::stdout().flush();
|
||||||
}
|
|
||||||
push_log_line(line);
|
push_log_line(line);
|
||||||
if let Ok(mut closed) = closed_windows().lock() {
|
if let Ok(mut closed) = closed_windows().lock() {
|
||||||
if !closed.iter().any(|(id, _)| *id == window_id) {
|
if !closed.iter().any(|(id, _)| *id == window_id) {
|
||||||
|
|
@ -481,10 +473,8 @@ mod imp {
|
||||||
/// away. Not a crash.
|
/// away. Not a crash.
|
||||||
pub fn note_user_closed_last_window() {
|
pub fn note_user_closed_last_window() {
|
||||||
let line = "[makepad-remote] app exit: user closed the last window".to_string();
|
let line = "[makepad-remote] app exit: user closed the last window".to_string();
|
||||||
if is_active() {
|
|
||||||
println!("{line}");
|
println!("{line}");
|
||||||
let _ = std::io::stdout().flush();
|
let _ = std::io::stdout().flush();
|
||||||
}
|
|
||||||
push_log_line(line);
|
push_log_line(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2138,10 +2128,6 @@ mod imp {
|
||||||
use crate::cx::Cx;
|
use crate::cx::Cx;
|
||||||
|
|
||||||
pub fn start_if_requested() {}
|
pub fn start_if_requested() {}
|
||||||
/// There is no remote bridge on these targets, so nothing ever asked for one.
|
|
||||||
pub fn requested() -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
pub fn is_active() -> bool {
|
pub fn is_active() -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,7 @@
|
||||||
//! scrollbar thumb by dragging is therefore deliberately NOT possible while
|
//! scrollbar thumb by dragging is therefore deliberately NOT possible while
|
||||||
//! exploded — a drag is the orbit — and that is the coexistence rule.
|
//! exploded — a drag is the orbit — and that is the coexistence rule.
|
||||||
//!
|
//!
|
||||||
//! F10 — once the dev overlays are switched on ([`crate::devtools`]) — tilts
|
//! F10 tilts the window into an isometric stack that renders **the component
|
||||||
//! the window into an isometric stack that renders **the component
|
|
||||||
//! nesting structure**: one plane per nesting level, siblings sharing a plane,
|
//! nesting structure**: one plane per nesting level, siblings sharing a plane,
|
||||||
//! children lifting toward the viewer and their parents staying at the bottom
|
//! children lifting toward the viewer and their parents staying at the bottom
|
||||||
//! of the stack. The point is to see — and click — the fully-covered parent
|
//! of the stack. The point is to see — and click — the fully-covered parent
|
||||||
|
|
@ -728,12 +727,7 @@ impl Cx {
|
||||||
}
|
}
|
||||||
match event {
|
match event {
|
||||||
Event::KeyDown(e) => {
|
Event::KeyDown(e) => {
|
||||||
// F10 is only ours when the app opted into the dev overlays
|
if e.key_code == KeyCode::F10 {
|
||||||
// (`--devtools` / `MAKEPAD_DEVTOOLS=1` / `--remote`). Otherwise
|
|
||||||
// it is the app's key like any other. `sploded_toggle` still
|
|
||||||
// works either way, so an app can put the mode on a key of its
|
|
||||||
// own choosing.
|
|
||||||
if e.key_code == KeyCode::F10 && crate::devtools::enabled() {
|
|
||||||
if e.is_repeat {
|
if e.is_repeat {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
//! ScreenCap — SHIFT+F12 records the window to an mp4, picture and sound.
|
//! ScreenCap — SHIFT+F12 records the window to an mp4, picture and sound.
|
||||||
//!
|
//!
|
||||||
//! The hotkey needs the dev overlays switched on
|
|
||||||
//! (`makepad_platform::devtools`: `--devtools`, `MAKEPAD_DEVTOOLS=1`, or
|
|
||||||
//! `--remote`); an app that wants its own recording key calls [`ScreenCap::toggle`].
|
|
||||||
//!
|
|
||||||
//! One widget, hardcoded into [`crate::window::Window`] the way the tweaker
|
//! One widget, hardcoded into [`crate::window::Window`] the way the tweaker
|
||||||
//! and the nav control are, so every Makepad app can record itself without
|
//! and the nav control are, so every Makepad app can record itself without
|
||||||
//! wiring anything up. Shift+F12 starts, Shift+F12 stops. While it records,
|
//! wiring anything up. Shift+F12 starts, Shift+F12 stops. While it records,
|
||||||
|
|
@ -47,7 +43,6 @@ use crate::makepad_draw::audio::AudioBuffer;
|
||||||
use crate::{makepad_derive_widget::*, makepad_draw::*, widget::*};
|
use crate::{makepad_derive_widget::*, makepad_draw::*, widget::*};
|
||||||
|
|
||||||
use makepad_platform::audio_output_tap::{add_audio_output_tap, remove_audio_output_tap};
|
use makepad_platform::audio_output_tap::{add_audio_output_tap, remove_audio_output_tap};
|
||||||
use makepad_platform::devtools;
|
|
||||||
use makepad_platform::screen_capture::{
|
use makepad_platform::screen_capture::{
|
||||||
add_screen_capture, remove_screen_capture, ScreenCaptureOptions,
|
add_screen_capture, remove_screen_capture, ScreenCaptureOptions,
|
||||||
};
|
};
|
||||||
|
|
@ -326,12 +321,7 @@ impl ScreenCap {
|
||||||
impl Widget for ScreenCap {
|
impl Widget for ScreenCap {
|
||||||
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
|
fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope) {
|
||||||
if let Event::KeyDown(ke) = event {
|
if let Event::KeyDown(ke) = event {
|
||||||
// The hotkey only exists once the dev overlays are switched on
|
if ke.key_code == self.hotkey
|
||||||
// (`--devtools` / `MAKEPAD_DEVTOOLS=1` / `--remote`). A shipped app
|
|
||||||
// should not have a key that starts writing mp4s to disk; one that
|
|
||||||
// wants a recorder can call `toggle` from its own binding.
|
|
||||||
if devtools::enabled()
|
|
||||||
&& ke.key_code == self.hotkey
|
|
||||||
&& ke.modifiers.shift == self.hotkey_shift
|
&& ke.modifiers.shift == self.hotkey_shift
|
||||||
&& !ke.is_repeat
|
&& !ke.is_repeat
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2653,8 +2653,12 @@ impl Widget for TextInput {
|
||||||
// In multiline mode, other modifier combos (Alt+Enter, or Ctrl+Enter
|
// In multiline mode, other modifier combos (Alt+Enter, or Ctrl+Enter
|
||||||
// on macOS) insert a newline below when not read-only.
|
// on macOS) insert a newline below when not read-only.
|
||||||
let has_physical_keyboard = cx.keyboard.has_physical_keyboard();
|
let has_physical_keyboard = cx.keyboard.has_physical_keyboard();
|
||||||
|
// Ctrl+Enter submits on every platform — on macOS `primary`
|
||||||
|
// is Cmd, and a person who reaches for Ctrl+Enter to send
|
||||||
|
// must not get a newline instead.
|
||||||
let should_submit = !self.is_multiline
|
let should_submit = !self.is_multiline
|
||||||
|| mods.is_primary()
|
|| mods.is_primary()
|
||||||
|
|| mods.control
|
||||||
|| (has_physical_keyboard && self.submit_on_enter && !mods.any());
|
|| (has_physical_keyboard && self.submit_on_enter && !mods.any());
|
||||||
if should_submit {
|
if should_submit {
|
||||||
cx.hide_text_ime();
|
cx.hide_text_ime();
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
//! The TWEAKER — the design-feedback overlay every `--remote` app grows.
|
//! The TWEAKER — the design-feedback overlay every `--remote` app grows.
|
||||||
//!
|
//!
|
||||||
//! Hardcoded into `Window` (like the caption bar: zero app wiring), inert
|
//! Hardcoded into `Window` (like the caption bar: zero app wiring), inert
|
||||||
//! unless the remote bridge is live, zero cost while off. The F12 key needs
|
//! unless the remote bridge is live, zero cost while off. Turned on (F12 or
|
||||||
//! the dev overlays switched on (`makepad_platform::devtools`: `--devtools`,
|
|
||||||
//! `MAKEPAD_DEVTOOLS=1`, or `--remote`); [`set_tweak_on`] is always there for
|
|
||||||
//! an app that wants to open the panel itself. Turned on (F12 or
|
|
||||||
//! `GET /tweak?on=1`), a person points at the UI and live-edits it while the
|
//! `GET /tweak?on=1`), a person points at the UI and live-edits it while the
|
||||||
//! AI watches the same session through the bridge:
|
//! AI watches the same session through the bridge:
|
||||||
//!
|
//!
|
||||||
|
|
@ -32,7 +29,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_box::{CheckBox, CheckBoxAction},
|
check_box::{CheckBox, CheckBoxAction},
|
||||||
fab_controls::{format_hex, parse_hex, rgb_to_hsv, FabColorPick, FabColorPickAction, FabValueInput, FabValueInputAction},
|
fab_controls::{format_hex, parse_hex, rgb_to_hsv, FabColorPick, FabColorPickAction, FabValueInput, FabValueInputAction},
|
||||||
makepad_draw::makepad_platform::devtools,
|
|
||||||
makepad_draw::makepad_platform::sploded::{SPLODED_SPREAD_DEFAULT, SPLODED_SPREAD_MAX, SPLODED_SPREAD_MIN},
|
makepad_draw::makepad_platform::sploded::{SPLODED_SPREAD_DEFAULT, SPLODED_SPREAD_MAX, SPLODED_SPREAD_MIN},
|
||||||
file_tree::{FileTree, FileTreeAction},
|
file_tree::{FileTree, FileTreeAction},
|
||||||
label::Label,
|
label::Label,
|
||||||
|
|
@ -620,19 +616,13 @@ pub fn window_intercept(
|
||||||
// F12 toggles the mode, bridge or no bridge: the design surface is
|
// F12 toggles the mode, bridge or no bridge: the design surface is
|
||||||
// in-process and owes the remote nothing. Only the HTTP endpoints and
|
// in-process and owes the remote nothing. Only the HTTP endpoints and
|
||||||
// the AI vibecode loop need --remote; without it they simply are not
|
// the AI vibecode loop need --remote; without it they simply are not
|
||||||
// there, and the panel still is. It does need the dev overlays to be
|
// there, and the panel still is.
|
||||||
// switched on though (`--devtools` / `MAKEPAD_DEVTOOLS=1` / `--remote`) —
|
|
||||||
// in a shipped app F12 belongs to the app, and `set_tweak_on` is still
|
|
||||||
// there for one that wants to open the panel itself.
|
|
||||||
//
|
//
|
||||||
// SHIFT+F12 is not ours: that is the screen recorder
|
// SHIFT+F12 is not ours: that is the screen recorder
|
||||||
// (widgets/src/screen_cap.rs), and it must not drag the design surface
|
// (widgets/src/screen_cap.rs), and it must not drag the design surface
|
||||||
// into every recording.
|
// into every recording.
|
||||||
if let Event::KeyDown(key_event) = event {
|
if let Event::KeyDown(key_event) = event {
|
||||||
if key_event.key_code == KeyCode::F12
|
if key_event.key_code == KeyCode::F12 && !key_event.modifiers.shift {
|
||||||
&& !key_event.modifiers.shift
|
|
||||||
&& devtools::enabled()
|
|
||||||
{
|
|
||||||
let flip = {
|
let flip = {
|
||||||
let mut s = session().lock().unwrap();
|
let mut s = session().lock().unwrap();
|
||||||
if s.toggle_event_id != cx.event_id() {
|
if s.toggle_event_id != cx.event_id() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue