test(gui-1b): step 3 witnesses the panel viewport, not the emitted event
The framing says it in as many words: "Not 'a PanelPointer was emitted' --- the observable effect on the panel's viewport." The row I wrote filtered and counted panel-pointer events, which is the blind spot the framing exists to close rather than the defect it guards against. It would have passed if the vertical axis emitted a horizontal gesture, if the receiver dropped what arrived, or if some other event accompanied a sub-threshold delta. Both halves now run in one row. The PRODUCER is this frontend's apply_wheel, reached through dispatch_window_event. The RECEIVER is a real pmacs::editor::EditorState with a live panel window, driven through classify_panel_pointer + apply_panel_pointer --- the pair the daemon itself calls --- and the assertion is the panel window's (view_top, view_left). Per axis: a sub-threshold delta puts nothing on the wire and moves the viewport by nothing; the delta that completes the notch moves it by exactly one step, on that axis and not the other. That needs the editor crate, so pmacs-gpu gains a DEV-dependency on pmacs --- test-only, never in the shipped graph --- and pmacs gains three #[doc(hidden)] test-support methods beside the ones already there: install_panel_view_for_test (which daemon.rs's own semantic_panel_view now delegates to, so there is one fixture rather than two), seed_window_buffer_for_test, and window_view_origin_for_test. Three things the row failed on before passing, each now a named setup fact rather than a silent dependency. The panel buffer starts empty and scroll_window clamps to line_count - 1, so an unseeded panel cannot scroll at all. Seeding it is not enough either: TextView caches the line partition it was built with, so the window has to be handed a rebuilt view. And the receiver re-derives the panel grid from an accepted geometry declaration --- without one every coordinate is outside a grid that does not exist and the gesture is Refused before it can do anything. The mutation that matters is the one no emission count could see: dropping PKind::ScrollLeft/ScrollRight from the daemon's panel arm --- the receiver half, the axis whose arm did not exist before B2 --- fires this row. So do rounding instead of banking, and collapsing the two axes into one accumulator. Gates: fmt; clippy --workspace --all-targets -D warnings; pmacs-gpu 322; --lib 2009; --lib --features crdt 2202; git diff --check.
This commit is contained in:
parent
a7006faf47
commit
e50f38ae20
|
|
@ -2627,6 +2627,7 @@ dependencies = [
|
|||
"env_logger",
|
||||
"glyphon",
|
||||
"loro",
|
||||
"pmacs",
|
||||
"pmacs-protocol",
|
||||
"pollster",
|
||||
"sys-locale",
|
||||
|
|
|
|||
|
|
@ -70,3 +70,8 @@ unicode-width = "0.2"
|
|||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
# TEST-ONLY, and never a runtime dependency: step 3's panel-wheel
|
||||
# witness has to observe the RECEIVER's effect, not the event this
|
||||
# frontend emits, because the defect it exists for is precisely
|
||||
# "the frontend emits and the receiver discards".
|
||||
pmacs = { path = ".." }
|
||||
|
|
|
|||
|
|
@ -21972,6 +21972,18 @@ mod tests {
|
|||
// Bottom panel Stage 2B-3 — the GPU band
|
||||
// ===================================================================
|
||||
|
||||
fn panel_frame_of_buffer(
|
||||
buffer_id: BufferId,
|
||||
rows: u32,
|
||||
cols: u32,
|
||||
geometry_epoch: u64,
|
||||
panel_epoch: u64,
|
||||
) -> PanelFrame {
|
||||
let mut frame = panel_frame_of(rows, cols, geometry_epoch, panel_epoch);
|
||||
frame.buffer_id = buffer_id;
|
||||
frame
|
||||
}
|
||||
|
||||
fn panel_frame_of(rows: u32, cols: u32, geometry_epoch: u64, panel_epoch: u64) -> PanelFrame {
|
||||
let cells = (0..(rows as usize * cols as usize))
|
||||
.map(|_| terminal_cell(pmacs_protocol::Glyph::Char('x'), CellStyle::default()))
|
||||
|
|
@ -22012,13 +22024,17 @@ mod tests {
|
|||
/// `Metrics`; a second surface declaration is suppressed by design
|
||||
/// and returns `None`.
|
||||
fn present_panel_in_harness(h: &mut EffectHarness, panel_epoch: u64) {
|
||||
present_panel_of_buffer(h, BufferId::from_raw(77), panel_epoch);
|
||||
}
|
||||
|
||||
fn present_panel_of_buffer(h: &mut EffectHarness, buffer_id: BufferId, panel_epoch: u64) {
|
||||
{
|
||||
let state = h.app.state.as_mut().expect("harness state");
|
||||
state.set_panel_wire(PANEL_MIN_VERSION);
|
||||
let (epoch, total) = state
|
||||
.next_geometry_declaration(GeometryTrigger::Metrics)
|
||||
.expect("metrics always advance the panel geometry epoch");
|
||||
let frame = panel_frame_of(4, total.cols.max(1), epoch, panel_epoch);
|
||||
let frame = panel_frame_of_buffer(buffer_id, 4, total.cols.max(1), epoch, panel_epoch);
|
||||
let _ = state.apply_attach_message(InstanceMessage::PanelFrame(
|
||||
PanelFramePayload::Present(frame),
|
||||
));
|
||||
|
|
@ -22031,36 +22047,51 @@ mod tests {
|
|||
let _ = h.read_until_sentinel();
|
||||
}
|
||||
|
||||
/// Step 3 — **the panel's fractional wheel, end to end, per axis.**
|
||||
/// The RECEIVER half of step 3: a real editor with a bottom panel,
|
||||
/// seeded with something to scroll in both directions, and with a
|
||||
/// frame geometry accepted.
|
||||
///
|
||||
/// Revision 20 sharpened this witness because its earlier form was
|
||||
/// satisfiable with the mechanism it protects entirely broken: a
|
||||
/// whole tick passes straight through #243's vertical receiver even
|
||||
/// if B1's accumulator discards every sub-tick it is given. So the
|
||||
/// row requires **fractional input** — a first sub-threshold delta
|
||||
/// produces **no** gesture, and accumulated same-panel deltas
|
||||
/// produce **exactly one** — and it requires that on **each axis**
|
||||
/// separately, because a single accumulator fed by both axes passes
|
||||
/// any one-axis row.
|
||||
/// Two things here are load-bearing, and each was found by the row
|
||||
/// failing without it. The panel buffer starts **empty**, and
|
||||
/// `scroll_window` clamps to `line_count - 1`, so an unseeded panel
|
||||
/// cannot scroll at all. And the receiver re-derives the panel grid
|
||||
/// from an accepted geometry declaration — without one, every
|
||||
/// coordinate is outside a grid that does not exist and the gesture
|
||||
/// is `Refused` before it can have any effect.
|
||||
fn panel_receiver() -> (
|
||||
pmacs::editor::EditorState,
|
||||
pmacs::protocol::FrontendId,
|
||||
pmacs::window::WindowId,
|
||||
BufferId,
|
||||
) {
|
||||
let editor = pmacs::editor::EditorState::new();
|
||||
let fid = pmacs::protocol::FrontendId(4242);
|
||||
let (_document, panel) = editor.install_panel_view_for_test(fid, true);
|
||||
let panel = panel.expect("the fixture installs a panel window");
|
||||
let wide_line = "w".repeat(400);
|
||||
editor.seed_window_buffer_for_test(panel, &format!("{wide_line}\n").repeat(50));
|
||||
let panel_buffer = editor
|
||||
.window_buffer_for_test(panel)
|
||||
.expect("the panel window has a buffer");
|
||||
let _ =
|
||||
editor.accept_semantic_frame_geometry(fid, 1, pmacs_protocol::CellSize::new(24, 80));
|
||||
(editor, fid, panel, panel_buffer)
|
||||
}
|
||||
|
||||
/// The PRODUCER half of step 3: this frontend, presenting a panel
|
||||
/// for the very buffer the receiver is showing — so the gestures it
|
||||
/// emits are about the same window the assertions read — with the
|
||||
/// pointer parked on a panel **cell**.
|
||||
///
|
||||
/// Driven through `dispatch_window_event` and observed on the wire,
|
||||
/// which is where a panel gesture actually goes.
|
||||
///
|
||||
/// *Mutations: round the notch instead of banking it → the
|
||||
/// sub-threshold legs (a 0.6 delta becomes a whole tick); bank into
|
||||
/// one accumulator per surface instead of per (surface, axis) → the
|
||||
/// second axis's first leg, which the first axis's leftover
|
||||
/// completes.*
|
||||
#[test]
|
||||
fn step3_a_panel_wheel_needs_a_whole_notch_on_each_axis() {
|
||||
/// Panel chrome banks nothing at all, and a probe that drifted onto
|
||||
/// it would satisfy every "nothing moved" assertion for entirely
|
||||
/// the wrong reason, so the target is asserted here.
|
||||
fn panel_producer(panel_buffer: BufferId) -> EffectHarness {
|
||||
use winit::dpi::PhysicalPosition;
|
||||
use winit::event::{DeviceId, MouseScrollDelta, TouchPhase};
|
||||
use winit::event::DeviceId;
|
||||
|
||||
let mut h = EffectHarness::new();
|
||||
present_panel_in_harness(&mut h, 1);
|
||||
|
||||
// A pixel inside the panel's content, so the wheel target is a
|
||||
// cell rather than the band's chrome.
|
||||
present_panel_of_buffer(&mut h, panel_buffer, 1);
|
||||
let (px, py, _, ph) = h
|
||||
.app
|
||||
.state
|
||||
|
|
@ -22078,63 +22109,145 @@ mod tests {
|
|||
h.app.classify_wheel_target(point.0, point.1),
|
||||
WheelTarget::PanelCell { .. }
|
||||
),
|
||||
"setup: the probe must be a panel CELL — panel chrome banks \
|
||||
nothing at all, and would satisfy every 'no gesture' \
|
||||
assertion below for the wrong reason"
|
||||
"setup: the probe must be a panel CELL"
|
||||
);
|
||||
h
|
||||
}
|
||||
|
||||
let mut wheel = |dx: f32, dy: f32| {
|
||||
h.feed(&WindowEvent::MouseWheel {
|
||||
device_id: DeviceId::dummy(),
|
||||
delta: MouseScrollDelta::LineDelta(dx, -dy),
|
||||
phase: TouchPhase::Moved,
|
||||
})
|
||||
/// Step 3 — **the panel wheel's END-TO-END effect, on both axes,
|
||||
/// driven by fractional input.**
|
||||
///
|
||||
/// The framing is explicit that an emission-only witness will not
|
||||
/// do: *"Not 'a `PanelPointer` was emitted' — the observable effect
|
||||
/// on the panel's viewport."* The defect it guards is exactly "the
|
||||
/// frontend emits and the receiver discards", and a row that counts
|
||||
/// emitted events reproduces that blind spot rather than catching
|
||||
/// it — it would pass if the vertical axis emitted a horizontal
|
||||
/// gesture, if the receiver dropped it, or if some other event
|
||||
/// accompanied a sub-threshold delta.
|
||||
///
|
||||
/// So this row runs both halves. The **producer** is this
|
||||
/// frontend's `apply_wheel`, reached through
|
||||
/// `dispatch_window_event`. The **receiver** is a real
|
||||
/// `pmacs::editor::EditorState` with a live panel window, driven
|
||||
/// through `classify_panel_pointer` + `apply_panel_pointer`, the
|
||||
/// pair the daemon itself calls. The assertion is the panel
|
||||
/// window's `(view_top, view_left)`.
|
||||
///
|
||||
/// Per axis: a first sub-threshold delta moves the viewport by
|
||||
/// **nothing** and puts **nothing** on the wire, and the delta that
|
||||
/// completes the notch moves it by **exactly one step** — once, not
|
||||
/// twice, and not the sum of everything banked.
|
||||
///
|
||||
/// *Mutations: round the notch instead of banking it → the
|
||||
/// sub-threshold legs; bank into one accumulator per surface rather
|
||||
/// than per (surface, axis) → the second axis's first leg, which
|
||||
/// the first axis's leftover completes; drop `PKind::ScrollLeft` /
|
||||
/// `ScrollRight` from the daemon's panel arm → the horizontal
|
||||
/// completion, which no emission count can see.*
|
||||
#[test]
|
||||
fn step3_a_panel_wheel_moves_the_panel_viewport_once_per_notch_per_axis() {
|
||||
use winit::event::{DeviceId, MouseScrollDelta, TouchPhase};
|
||||
|
||||
let (mut editor, fid, panel, panel_buffer) = panel_receiver();
|
||||
let origin = |editor: &pmacs::editor::EditorState| {
|
||||
editor
|
||||
.window_view_origin_for_test(panel)
|
||||
.expect("the panel window is live")
|
||||
};
|
||||
let gestures = |step: &Step| {
|
||||
assert_eq!(origin(&editor), (0, 0), "setup: the panel starts home");
|
||||
|
||||
let mut h = panel_producer(panel_buffer);
|
||||
|
||||
// One turn of the wheel, carried all the way through: the
|
||||
// frontend's events are replayed into the editor exactly as the
|
||||
// daemon replays them.
|
||||
let turn =
|
||||
|h: &mut EffectHarness, editor: &mut pmacs::editor::EditorState, dx: f32, dy: f32| {
|
||||
let step = h.feed(&WindowEvent::MouseWheel {
|
||||
device_id: DeviceId::dummy(),
|
||||
delta: MouseScrollDelta::LineDelta(dx, -dy),
|
||||
phase: TouchPhase::Moved,
|
||||
});
|
||||
let mut replayed = 0usize;
|
||||
for event in &step.outbound {
|
||||
if let pmacs_protocol::FrontendEvent::PanelPointer {
|
||||
buffer_id,
|
||||
coord,
|
||||
kind,
|
||||
mods,
|
||||
..
|
||||
} = event
|
||||
{
|
||||
let disposition =
|
||||
editor.classify_panel_pointer(fid, *buffer_id, *coord, *kind);
|
||||
editor.apply_panel_pointer(fid, &disposition, *coord, *kind, *mods);
|
||||
replayed += 1;
|
||||
}
|
||||
}
|
||||
(step, replayed)
|
||||
};
|
||||
|
||||
// Vertical, sub-threshold: nothing on the wire, nothing moves.
|
||||
let (step, replayed) = turn(&mut h, &mut editor, 0.0, 0.6);
|
||||
assert!(
|
||||
step.outbound.is_empty(),
|
||||
"a sub-threshold vertical delta must put NOTHING on the \
|
||||
wire, got {:?}",
|
||||
step.outbound
|
||||
.iter()
|
||||
.filter(|e| {
|
||||
matches!(
|
||||
e,
|
||||
pmacs_protocol::FrontendEvent::PanelPointer { .. }
|
||||
| pmacs_protocol::FrontendEvent::PanelPointerMapped { .. }
|
||||
)
|
||||
})
|
||||
.count()
|
||||
};
|
||||
|
||||
// Vertical: 0.6 of a notch is not a notch.
|
||||
let step = wheel(0.0, 0.6);
|
||||
assert_eq!(
|
||||
gestures(&step),
|
||||
0,
|
||||
"a sub-threshold vertical delta must reach the panel as \
|
||||
nothing at all"
|
||||
);
|
||||
// Horizontal, before the vertical bank is completed: if the two
|
||||
// axes shared one accumulator, this 0.6 would finish the
|
||||
// vertical 0.6 and fire here.
|
||||
let step = wheel(0.6, 0.0);
|
||||
assert_eq!(replayed, 0);
|
||||
assert_eq!(
|
||||
gestures(&step),
|
||||
0,
|
||||
"and a sub-threshold horizontal delta must not be completed \
|
||||
by the vertical one banked before it"
|
||||
origin(&editor),
|
||||
(0, 0),
|
||||
"and the panel viewport must not move"
|
||||
);
|
||||
|
||||
// Completing each axis produces exactly one gesture, not two,
|
||||
// and not the sum of everything banked so far.
|
||||
let step = wheel(0.0, 0.6);
|
||||
assert_eq!(
|
||||
gestures(&step),
|
||||
1,
|
||||
"0.6 + 0.6 is one vertical notch, and exactly one"
|
||||
// Horizontal, sub-threshold, with the vertical bank still
|
||||
// standing: one accumulator fed by both axes would complete
|
||||
// here and scroll.
|
||||
let (step, _) = turn(&mut h, &mut editor, 0.6, 0.0);
|
||||
assert!(
|
||||
step.outbound.is_empty(),
|
||||
"a sub-threshold horizontal delta must not be completed by \
|
||||
the vertical one banked before it, got {:?}",
|
||||
step.outbound
|
||||
);
|
||||
let step = wheel(0.6, 0.0);
|
||||
assert_eq!(
|
||||
gestures(&step),
|
||||
1,
|
||||
"and the horizontal axis completes on its own count"
|
||||
origin(&editor),
|
||||
(0, 0),
|
||||
"and still nothing has moved on either axis"
|
||||
);
|
||||
|
||||
// Completing the vertical notch: the viewport moves ONE step
|
||||
// down, and the horizontal origin stays put.
|
||||
let (_, replayed) = turn(&mut h, &mut editor, 0.0, 0.6);
|
||||
assert_eq!(replayed, 1, "one notch is one gesture");
|
||||
let after_vertical = origin(&editor);
|
||||
assert!(
|
||||
after_vertical.0 > 0,
|
||||
"the completed vertical notch must scroll the panel"
|
||||
);
|
||||
assert_eq!(
|
||||
after_vertical.1, 0,
|
||||
"and must not move it sideways: a vertical notch that \
|
||||
emitted a horizontal gesture would show up exactly here"
|
||||
);
|
||||
|
||||
// Completing the horizontal notch: sideways this time, and the
|
||||
// vertical origin does not move again.
|
||||
let (_, replayed) = turn(&mut h, &mut editor, 0.6, 0.0);
|
||||
assert_eq!(replayed, 1, "one notch is one gesture");
|
||||
let after_horizontal = origin(&editor);
|
||||
assert!(
|
||||
after_horizontal.1 > 0,
|
||||
"the completed horizontal notch must scroll the panel \
|
||||
sideways — the axis whose receiver arm did not exist before \
|
||||
B2, and which no emission count can see"
|
||||
);
|
||||
assert_eq!(
|
||||
after_horizontal.0, after_vertical.0,
|
||||
"and must not scroll it vertically a second time"
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6416,58 +6416,9 @@ mod tests {
|
|||
fid: FrontendId,
|
||||
with_panel: bool,
|
||||
) -> (crate::window::WindowId, Option<crate::window::WindowId>) {
|
||||
use crate::window::{FrontendView, Layout, LayoutNode, Orientation, Window, WindowParams};
|
||||
|
||||
let mut core = editor.core.borrow_mut();
|
||||
let doc_buf = core.active_window().buffer_id;
|
||||
let document = crate::window::WindowId::next();
|
||||
let doc_view = {
|
||||
let reg = core.registry.borrow();
|
||||
crate::text_view::TextView::new(reg.get(doc_buf).expect("doc"))
|
||||
};
|
||||
core.windows
|
||||
.insert(document, Window::new(document, doc_buf, doc_view));
|
||||
let panel = with_panel.then(|| {
|
||||
let panel_buf = core.registry.borrow_mut().create("*panel*");
|
||||
let panel_id = crate::window::WindowId::next();
|
||||
let panel_view = {
|
||||
let reg = core.registry.borrow();
|
||||
crate::text_view::TextView::new(reg.get(panel_buf).expect("panel"))
|
||||
};
|
||||
let mut window = Window::new(panel_id, panel_buf, panel_view);
|
||||
let mut params = WindowParams::default();
|
||||
params.side = Some(crate::window::Side::Bottom);
|
||||
params.fixed_rows = Some(4);
|
||||
window.params = params;
|
||||
core.windows.insert(panel_id, window);
|
||||
panel_id
|
||||
});
|
||||
let layout = match panel {
|
||||
Some(panel) => Layout {
|
||||
root: LayoutNode::Split {
|
||||
orientation: Orientation::Horizontal,
|
||||
children: vec![LayoutNode::Leaf(document), LayoutNode::Leaf(panel)],
|
||||
weights: vec![1, 1],
|
||||
},
|
||||
},
|
||||
None => Layout::single(document),
|
||||
};
|
||||
core.register_frontend_view(
|
||||
fid,
|
||||
FrontendView {
|
||||
layout,
|
||||
active: document,
|
||||
fold_projection: false,
|
||||
// Stage 2B-2 is dark: production negotiation still sets
|
||||
// this `false` for every semantic session, so the
|
||||
// projection is exercised through a test-only view (the
|
||||
// framing's §7.2.2 posture).
|
||||
panel_capable: true,
|
||||
frame_geometry: None,
|
||||
panel_hidden: false,
|
||||
},
|
||||
);
|
||||
(document, panel)
|
||||
// One fixture, shared with `pmacs-gpu`'s step-3 effect witness,
|
||||
// which needs a real panel window to observe an effect on.
|
||||
editor.install_panel_view_for_test(fid, with_panel)
|
||||
}
|
||||
|
||||
fn session(version: u32, semantic: bool) -> crate::presence::SessionState {
|
||||
|
|
|
|||
116
src/editor.rs
116
src/editor.rs
|
|
@ -2956,6 +2956,122 @@ impl EditorState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Install a frontend view with a bottom panel, for tests that need
|
||||
/// a real panel window to observe an effect on.
|
||||
///
|
||||
/// **Test support, not production.** It exists because the panel
|
||||
/// receiver's effect is only observable against a live side window,
|
||||
/// and `pmacs-gpu`'s step-3 witness has to see that effect rather
|
||||
/// than the event it emits — the defect it guards is precisely
|
||||
/// "the frontend emits and the receiver discards", which an
|
||||
/// emission-only row reproduces instead of catching.
|
||||
///
|
||||
/// Returns `(document, panel)`.
|
||||
#[doc(hidden)]
|
||||
pub fn install_panel_view_for_test(
|
||||
&self,
|
||||
frontend_id: FrontendId,
|
||||
with_panel: bool,
|
||||
) -> (WindowId, Option<WindowId>) {
|
||||
use crate::window::{FrontendView, Layout, LayoutNode, Orientation, Window, WindowParams};
|
||||
|
||||
let mut core = self.core.borrow_mut();
|
||||
let doc_buf = core.active_window().buffer_id;
|
||||
let document = WindowId::next();
|
||||
let doc_view = {
|
||||
let reg = core.registry.borrow();
|
||||
crate::text_view::TextView::new(reg.get(doc_buf).expect("doc"))
|
||||
};
|
||||
core.windows
|
||||
.insert(document, Window::new(document, doc_buf, doc_view));
|
||||
let panel = with_panel.then(|| {
|
||||
let panel_buf = core.registry.borrow_mut().create("*panel*");
|
||||
let panel_id = WindowId::next();
|
||||
let panel_view = {
|
||||
let reg = core.registry.borrow();
|
||||
crate::text_view::TextView::new(reg.get(panel_buf).expect("panel"))
|
||||
};
|
||||
let mut window = Window::new(panel_id, panel_buf, panel_view);
|
||||
let mut params = WindowParams::default();
|
||||
params.side = Some(crate::window::Side::Bottom);
|
||||
params.fixed_rows = Some(4);
|
||||
window.params = params;
|
||||
core.windows.insert(panel_id, window);
|
||||
panel_id
|
||||
});
|
||||
let layout = match panel {
|
||||
Some(panel) => Layout {
|
||||
root: LayoutNode::Split {
|
||||
orientation: Orientation::Horizontal,
|
||||
children: vec![LayoutNode::Leaf(document), LayoutNode::Leaf(panel)],
|
||||
weights: vec![1, 1],
|
||||
},
|
||||
},
|
||||
None => Layout::single(document),
|
||||
};
|
||||
core.register_frontend_view(
|
||||
frontend_id,
|
||||
FrontendView {
|
||||
layout,
|
||||
active: document,
|
||||
fold_projection: false,
|
||||
panel_capable: true,
|
||||
frame_geometry: None,
|
||||
panel_hidden: false,
|
||||
},
|
||||
);
|
||||
(document, panel)
|
||||
}
|
||||
|
||||
/// Replace a window's buffer contents, so a test can give a panel
|
||||
/// something to scroll. Without it the `*panel*` buffer is empty
|
||||
/// and every scroll clamps to zero — a viewport row against it
|
||||
/// would measure nothing.
|
||||
#[doc(hidden)]
|
||||
pub fn seed_window_buffer_for_test(&self, win_id: WindowId, text: &str) {
|
||||
let core = self.core.borrow();
|
||||
let Some(buffer_id) = core.windows.get(&win_id).map(|w| w.buffer_id) else {
|
||||
return;
|
||||
};
|
||||
let registry = core.registry.clone();
|
||||
drop(core);
|
||||
{
|
||||
let mut reg = registry.borrow_mut();
|
||||
if let Ok(buf) = reg.get_mut(buffer_id) {
|
||||
let _ = buf.set_generated_contents(text.as_bytes());
|
||||
}
|
||||
}
|
||||
// **And rebuild the window's view.** `TextView` caches the line
|
||||
// partition it was built with, and `scroll_window` reads its
|
||||
// `line_count` — so a window left holding the pre-seed view
|
||||
// clamps every scroll to zero and a viewport row against it
|
||||
// measures nothing.
|
||||
let view = {
|
||||
let reg = registry.borrow();
|
||||
reg.get(buffer_id).ok().map(crate::text_view::TextView::new)
|
||||
};
|
||||
if let Some(view) = view
|
||||
&& let Some(window) = self.core.borrow_mut().windows.get_mut(&win_id)
|
||||
{
|
||||
window.text_view = view;
|
||||
}
|
||||
}
|
||||
|
||||
/// A window's viewport origin, `(view_top, view_left)` — the pair a
|
||||
/// panel-wheel effect moves.
|
||||
#[doc(hidden)]
|
||||
pub fn window_view_origin_for_test(&self, win_id: WindowId) -> Option<(usize, u32)> {
|
||||
let core = self.core.borrow();
|
||||
let window = core.windows.get(&win_id)?;
|
||||
Some((window.view_top, window.view_left))
|
||||
}
|
||||
|
||||
/// The buffer a window is showing.
|
||||
#[doc(hidden)]
|
||||
pub fn window_buffer_for_test(&self, win_id: WindowId) -> Option<crate::buffer::BufferId> {
|
||||
Some(self.core.borrow().windows.get(&win_id)?.buffer_id)
|
||||
}
|
||||
|
||||
/// Classify an authenticated panel gesture, WITHOUT applying it
|
||||
/// (Q#BP-R4).
|
||||
///
|
||||
|
|
|
|||
Loading…
Reference in New Issue