T44d Web · Hidden demos keep still, wide ones scroll whole, pastes read the web's clipboard
This commit is contained in:
26 files changed
+211
-101
No files matched your search
@@ -161,6 +161,8 @@ impl CodeEditor {
|
||||
div()
|
||||
.w(advance * digits as f32)
|
||||
.text_right()
|
||||
// Snapped to device pixels, the box can run short.
|
||||
.whitespace_nowrap()
|
||||
.when(current, |number| number.text_color(colors.fg))
|
||||
.child(number),
|
||||
)
|
||||
|
||||
+1
-1
@@ -97,7 +97,7 @@ pub(crate) fn bind_keys(cx: &mut App) {
|
||||
KeyBinding::new("secondary-alt-[", Fold, context),
|
||||
KeyBinding::new("secondary-alt-]", Unfold, context),
|
||||
];
|
||||
// The browser's paste event carries the clipboard to the input handler.
|
||||
// The browser's paste event reaches the input handler.
|
||||
if !cfg!(target_family = "wasm") {
|
||||
bindings.push(KeyBinding::new("secondary-v", Paste, context));
|
||||
}
|
||||
|
||||
+9
-2
@@ -112,6 +112,8 @@ pub struct CodeEditor {
|
||||
pub(crate) metrics: Metrics,
|
||||
pub(crate) dragging: bool,
|
||||
pub(crate) caret_on: bool,
|
||||
/// Blink runs only while focused, or unseen cursors redraw forever.
|
||||
focused: bool,
|
||||
pub(crate) chat_field: gpui::Entity<TextInput>,
|
||||
pub(crate) frame: Frame,
|
||||
blink_epoch: u64,
|
||||
@@ -133,8 +135,12 @@ impl CodeEditor {
|
||||
let chat_field =
|
||||
cx.new(|cx| TextInput::new(window, cx).placeholder("Ask to change this code"));
|
||||
let subscriptions = vec![
|
||||
cx.on_focus(&focus, window, |editor, _, cx| editor.restart_blink(cx)),
|
||||
cx.on_focus(&focus, window, |editor, _, cx| {
|
||||
editor.focused = true;
|
||||
editor.restart_blink(cx);
|
||||
}),
|
||||
cx.on_blur(&focus, window, |editor, _, cx| {
|
||||
editor.focused = false;
|
||||
editor.dragging = false;
|
||||
editor._blink = None;
|
||||
cx.notify();
|
||||
@@ -178,6 +184,7 @@ impl CodeEditor {
|
||||
metrics: Metrics::default(),
|
||||
dragging: false,
|
||||
caret_on: true,
|
||||
focused: false,
|
||||
chat_field,
|
||||
frame: Frame::default(),
|
||||
blink_epoch: 0,
|
||||
@@ -462,7 +469,7 @@ impl CodeEditor {
|
||||
self.blink_epoch += 1;
|
||||
let epoch = self.blink_epoch;
|
||||
let still = cx.theme().reduced_motion;
|
||||
self._blink = (!still).then(|| {
|
||||
self._blink = (!still && self.focused).then(|| {
|
||||
cx.spawn(async move |editor, cx| {
|
||||
loop {
|
||||
cx.background_executor().timer(BLINK).await;
|
||||
|
||||
+28
-1
@@ -1,4 +1,4 @@
|
||||
use std::time::Duration;
|
||||
use std::{cell::Cell, rc::Rc, time::Duration};
|
||||
|
||||
use gpui::{
|
||||
ClipboardItem, Entity, EntityInputHandler, KeyUpEvent, Keystroke, TestAppContext,
|
||||
@@ -256,3 +256,30 @@ fn a_browser_paste_gives_each_cursor_its_line(cx: &mut TestAppContext) {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn only_a_focused_editor_blinks(cx: &mut TestAppContext) {
|
||||
let (editor, cx) = editor("a\nb", cx);
|
||||
let redraws = Rc::new(Cell::new(0));
|
||||
let counted = redraws.clone();
|
||||
let _count = cx.update(|_, cx| cx.observe(&editor, move |_, _| counted.set(counted.get() + 1)));
|
||||
editor.update(cx, |editor, cx| {
|
||||
let second = 2..2;
|
||||
editor.select([second], cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
redraws.set(0);
|
||||
cx.executor().advance_clock(Duration::from_secs(3));
|
||||
cx.run_until_parked();
|
||||
assert_eq!(redraws.get(), 0, "an unfocused editor keeps still");
|
||||
cx.update(|window, cx| {
|
||||
window.activate_window();
|
||||
let focus = editor.read(cx).focus.clone();
|
||||
focus.focus(window, cx);
|
||||
});
|
||||
cx.run_until_parked();
|
||||
redraws.set(0);
|
||||
cx.executor().advance_clock(Duration::from_secs(3));
|
||||
cx.run_until_parked();
|
||||
assert!(redraws.get() > 2, "a focused one blinks");
|
||||
}
|
||||
@@ -89,7 +89,7 @@ pub(crate) fn bind_keys(cx: &mut App) {
|
||||
KeyBinding::new("secondary-enter", Submit, Some(CONTEXT)),
|
||||
KeyBinding::new("secondary-enter", Submit, Some(FORM_CONTEXT)),
|
||||
];
|
||||
// The browser's paste event carries the clipboard to the input handler.
|
||||
// The browser's paste event reaches the input handler.
|
||||
if !cfg!(target_family = "wasm") {
|
||||
bindings.push(KeyBinding::new("secondary-v", Paste, Some(CONTEXT)));
|
||||
}
|
||||
|
||||
+22
-10
@@ -1,6 +1,9 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use gpui::{App, ClipboardItem, Entity, KeyDownEvent, Pixels, Point, SharedString, Window, point};
|
||||
use anyhow::anyhow;
|
||||
use gpui::{
|
||||
App, ClipboardItem, Entity, KeyDownEvent, Pixels, Point, SharedString, TaskExt, Window, point,
|
||||
};
|
||||
|
||||
use super::{
|
||||
formula::filled,
|
||||
@@ -283,15 +286,24 @@ pub(crate) fn keys(
|
||||
sheet.read(cx),
|
||||
))),
|
||||
("v", true) => {
|
||||
let Some(text) = cx.read_from_clipboard().and_then(|item| item.text()) else {
|
||||
log::info!("grid: the clipboard holds no text to paste");
|
||||
return;
|
||||
};
|
||||
let edits = pasted(&text, (row, col), (rows, cols));
|
||||
log::info!("grid: pasted {} cells", edits.len());
|
||||
if let Some(on_change) = &face.on_change {
|
||||
on_change(&edits, window, cx);
|
||||
}
|
||||
// The web reads its clipboard only through the async reader.
|
||||
let read = cx.read_from_clipboard_async();
|
||||
let on_change = face.on_change.clone();
|
||||
window
|
||||
.spawn(cx, async move |cx| {
|
||||
let item = read.await.map_err(|error| anyhow!("{error:?}"))?;
|
||||
let Some(text) = item.and_then(|item| item.text()) else {
|
||||
log::info!("grid: the clipboard holds no text to paste");
|
||||
return Ok(());
|
||||
};
|
||||
let edits = pasted(&text, (row, col), (rows, cols));
|
||||
log::info!("grid: pasted {} cells", edits.len());
|
||||
if let Some(on_change) = on_change {
|
||||
cx.update(|window, cx| on_change(&edits, window, cx))?;
|
||||
}
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.detach_and_log_err(cx);
|
||||
}
|
||||
_ => {
|
||||
let typed = stroke.key_char.clone().filter(|typed| {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gpui::{ClipboardItem, Modifiers, TestAppContext, point, px};
|
||||
|
||||
use super::{Grid, settle};
|
||||
use crate::theme::Theme;
|
||||
|
||||
#[gpui::test]
|
||||
fn a_paste_fills_cells_from_the_cursor(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
Theme::init(cx);
|
||||
crate::forms::bind_keys(cx);
|
||||
});
|
||||
let heard = Rc::new(RefCell::new(Vec::new()));
|
||||
let seen = heard.clone();
|
||||
let (_, cx) = cx.add_window_view(|_, _| Grid(seen));
|
||||
settle(cx);
|
||||
cx.simulate_click(point(px(48.0), px(42.0)), Modifiers::none());
|
||||
settle(cx);
|
||||
cx.update(|_, cx| cx.write_to_clipboard(ClipboardItem::new_string("x\ty\nz\tw".into())));
|
||||
cx.simulate_keystrokes("cmd-v");
|
||||
settle(cx);
|
||||
let cells = |row, col, text: &str| (row, col, text.to_string());
|
||||
assert_eq!(
|
||||
*heard.borrow(),
|
||||
[
|
||||
cells(0, 0, "x"),
|
||||
cells(0, 1, "y"),
|
||||
cells(1, 0, "z"),
|
||||
cells(1, 1, "w")
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -9,6 +9,7 @@ use gpui::{
|
||||
Styled, TestAppContext, VisualTestContext, Window, div, point, px,
|
||||
};
|
||||
|
||||
mod clipboard;
|
||||
mod columns;
|
||||
|
||||
use super::{Column, DataTable, FilterBuilder, FilterRule, Row};
|
||||
|
||||
@@ -90,7 +90,7 @@ pub(crate) fn printed(source: &str) -> (String, Vec<(Range<usize>, Pen)>) {
|
||||
(printed.text, printed.spans)
|
||||
}
|
||||
|
||||
/// Plain text sets no colors with OSC 4, 10 or 11.
|
||||
/// Plain text sets no colors through OSC 4, 10, 11.
|
||||
struct Unset;
|
||||
|
||||
impl Index<usize> for Unset {
|
||||
|
||||
@@ -93,7 +93,7 @@ impl Ink {
|
||||
}
|
||||
}
|
||||
|
||||
/// Colors a program set with OSC 4, 10 or 11, by index.
|
||||
/// Colors a program set through OSC 4, 10, 11.
|
||||
pub(crate) trait Set: Index<usize, Output = Option<Rgb>> {}
|
||||
|
||||
impl<T: Index<usize, Output = Option<Rgb>>> Set for T {}
|
||||
|
||||
+1
-1
@@ -136,7 +136,7 @@ impl Theme {
|
||||
Self::fade_to_target(cx);
|
||||
}
|
||||
|
||||
/// Sets `mode` without the fade, as before a window's first frame; a fade under way stops.
|
||||
/// Sets `mode` at once, stopping any fade under way.
|
||||
pub fn set_mode_now(mode: Mode, cx: &mut App) {
|
||||
log::info!("theme: mode -> {mode:?}, at once");
|
||||
let theme = cx.global_mut::<Theme>();
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ fn an_owners_palette_serves_its_mode_alone(cx: &mut TestAppContext) {
|
||||
assert_eq!(cx.read(|cx| cx.theme().palette()), Palette::light(false));
|
||||
}
|
||||
|
||||
/// A mode set at once wears its palette at once, and a fade under way cannot pull it back.
|
||||
/// A mode set at once keeps its palette through fades.
|
||||
#[gpui::test]
|
||||
fn a_mode_set_at_once_skips_the_fade(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
|
||||
Reference in new issue
Block a user