mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-08 03:19:42 -06:00
Refuse synthetic control-channel input as OK in Open Hyperlink (#2181)
A ui.key Enter or ui.click on OK reached egui as ordinary input and could confirm the openLink prompt. A pass that carries synthetic input now keeps the prompt open and opens nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
94dd42ba31
commit
fe062f57a6
@@ -1194,6 +1194,30 @@ mod tests {
|
||||
assert_eq!(opened.borrow().len(), 1);
|
||||
}
|
||||
|
||||
/// #2181: an OK that arrives with synthetic control-channel input (`ui.key` Enter, `ui.click`
|
||||
/// on OK) opens nothing and keeps the prompt open; the same OK from the user opens the link.
|
||||
#[test]
|
||||
fn synthetic_input_cannot_confirm_open_hyperlink() {
|
||||
use show::{SYNTHETIC_LINK_CONFIRM, confirm_open_link};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
let opened = Rc::new(RefCell::new(Vec::<String>::new()));
|
||||
let sink = opened.clone();
|
||||
let services = crate::Services { open_url: Some(Box::new(move |u: &str| sink.borrow_mut().push(u.to_string()))), ..Default::default() };
|
||||
let mut session = gridcraft_engine::Session::default();
|
||||
session.new_workbook();
|
||||
let mut app = SheetApp::new(session, services);
|
||||
let d = Dialog::open(&mut app, "openLink", json!({"url": "https://example.org/a"})).unwrap();
|
||||
|
||||
app.synthetic_pending = true;
|
||||
assert!(!confirm_open_link(&mut app, &d), "the prompt stays open");
|
||||
assert!(opened.borrow().is_empty(), "synthetic input opens nothing");
|
||||
assert_eq!(app.message.as_ref().map(|m| m.1.as_str()), Some(SYNTHETIC_LINK_CONFIRM));
|
||||
|
||||
app.synthetic_pending = false;
|
||||
app.message = None;
|
||||
assert!(confirm_open_link(&mut app, &d), "the user's OK closes the prompt");
|
||||
assert_eq!(*opened.borrow(), vec!["https://example.org/a".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_validation_without_a_rule_opens_on_a_blank_list() {
|
||||
|
||||
@@ -409,9 +409,8 @@ pub fn show(app: &mut SheetApp, ctx: &egui::Context) {
|
||||
}
|
||||
if confirm && d.name == "openLink" {
|
||||
confirm = false;
|
||||
open = false;
|
||||
if let Err(e) = open_confirmed_link(app, &d) {
|
||||
app.message = Some((d.title.clone(), crate::clean_error(&e)));
|
||||
if confirm_open_link(app, &d) {
|
||||
open = false;
|
||||
}
|
||||
}
|
||||
if confirm && d.command.is_some() && home_sheet_deleted(app, &mut d) {
|
||||
@@ -492,6 +491,24 @@ pub fn show(app: &mut SheetApp, ctx: &egui::Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/// OK in Open Hyperlink; returns whether the prompt closes. A pass that carries control-channel
|
||||
/// input (`ui.key`, `ui.click`, `ui.drag`) can't say yes for the user (#2181): the link stays
|
||||
/// unopened and the prompt open, with a message. Real input in that same pass is refused too, so
|
||||
/// the user just presses OK again.
|
||||
pub(super) fn confirm_open_link(app: &mut SheetApp, d: &Dialog) -> bool {
|
||||
if app.synthetic_pending {
|
||||
app.message = Some((d.title.clone(), SYNTHETIC_LINK_CONFIRM.into()));
|
||||
return false;
|
||||
}
|
||||
if let Err(e) = open_confirmed_link(app, d) {
|
||||
app.message = Some((d.title.clone(), crate::clean_error(&e)));
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Why Open Hyperlink ignored an OK that arrived with synthetic control-channel input.
|
||||
pub(super) const SYNTHETIC_LINK_CONFIRM: &str = "Synthetic input from the control channel can't open a link: press OK yourself to open it.";
|
||||
|
||||
/// The user pressed OK in Open Hyperlink: opens the address the prompt showed (`d.link`), never
|
||||
/// one a caller edited into its values afterwards.
|
||||
pub(super) fn open_confirmed_link(app: &mut SheetApp, d: &Dialog) -> Result<(), String> {
|
||||
|
||||
Reference in New Issue
Block a user