Skip to content

Commit

Permalink
Merge branch 'emilk:master' into patch3
Browse files Browse the repository at this point in the history
  • Loading branch information
rustbasic authored Jan 25, 2024
2 parents bcfb054 + 5388e65 commit a367c89
Show file tree
Hide file tree
Showing 24 changed files with 822 additions and 182 deletions.
16 changes: 3 additions & 13 deletions crates/eframe/src/native/glow_integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,6 @@ use super::{
*,
};

// Note: that the current Glutin API design tightly couples the GL context with
// the Window which means it's not practically possible to just destroy the
// window and re-create a new window while continuing to use the same GL context.
//
// For now this means it's not possible to support Android as well as we can with
// wgpu because we're basically forced to destroy and recreate _everything_ when
// the application suspends and resumes.
//
// There is work in progress to improve the Glutin API so it has a separate Surface
// API that would allow us to just destroy a Window/Surface when suspending, see:
// https://github.com/rust-windowing/glutin/pull/1435

// ----------------------------------------------------------------------------
// Types:

Expand Down Expand Up @@ -525,7 +513,9 @@ impl GlowWinitRunning {
let mut glutin = self.glutin.borrow_mut();
let egui_ctx = glutin.egui_ctx.clone();
let viewport = glutin.viewports.get_mut(&viewport_id).unwrap();
let window = viewport.window.as_ref().unwrap();
let Some(window) = viewport.window.as_ref() else {
return EventResult::Wait;
};
egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window);

let egui_winit = viewport.egui_winit.as_mut().unwrap();
Expand Down
19 changes: 11 additions & 8 deletions crates/egui/src/containers/scroll_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ impl ScrollArea {
}
}
} else {
// Kinetic scrolling
let stop_speed = 20.0; // Pixels per second.
let friction_coeff = 1000.0; // Pixels per second squared.
let dt = ui.input(|i| i.unstable_dt);
Expand Down Expand Up @@ -781,12 +782,12 @@ impl Prepared {
&& scroll_enabled[0] != scroll_enabled[1];
for d in 0..2 {
if scroll_enabled[d] {
let scroll_delta = ui.ctx().frame_state(|fs| {
let scroll_delta = ui.ctx().input_mut(|input| {
if always_scroll_enabled_direction {
// no bidirectional scrolling; allow horizontal scrolling without pressing shift
fs.scroll_delta[0] + fs.scroll_delta[1]
input.smooth_scroll_delta[0] + input.smooth_scroll_delta[1]
} else {
fs.scroll_delta[d]
input.smooth_scroll_delta[d]
}
});

Expand All @@ -795,15 +796,17 @@ impl Prepared {

if scrolling_up || scrolling_down {
state.offset[d] -= scroll_delta;
// Clear scroll delta so no parent scroll will use it.
ui.ctx().frame_state_mut(|fs| {

// Clear scroll delta so no parent scroll will use it:
ui.ctx().input_mut(|input| {
if always_scroll_enabled_direction {
fs.scroll_delta[0] = 0.0;
fs.scroll_delta[1] = 0.0;
input.smooth_scroll_delta[0] = 0.0;
input.smooth_scroll_delta[1] = 0.0;
} else {
fs.scroll_delta[d] = 0.0;
input.smooth_scroll_delta[d] = 0.0;
}
});

state.scroll_stuck_to_end[d] = false;
}
}
Expand Down
23 changes: 20 additions & 3 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ impl Context {
/// ```
pub fn begin_frame(&self, new_input: RawInput) {
crate::profile_function!();

crate::text_selection::LabelSelectionState::begin_frame(self);
self.write(|ctx| ctx.begin_frame_mut(new_input));
}
}
Expand Down Expand Up @@ -697,10 +697,16 @@ impl Context {

/// Read-write access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
#[inline]
pub(crate) fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
pub fn graphics_mut<R>(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R {
self.write(move |ctx| writer(&mut ctx.viewport().graphics))
}

/// Read-only access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to.
#[inline]
pub fn graphics<R>(&self, reader: impl FnOnce(&GraphicLayers) -> R) -> R {
self.write(move |ctx| reader(&ctx.viewport().graphics))
}

/// Read-only access to [`PlatformOutput`].
///
/// This is what egui outputs each frame.
Expand Down Expand Up @@ -1610,6 +1616,8 @@ impl Context {
crate::gui_zoom::zoom_with_keyboard(self);
}

crate::text_selection::LabelSelectionState::end_frame(self);

let debug_texts = self.write(|ctx| std::mem::take(&mut ctx.debug_texts));
if !debug_texts.is_empty() {
// Show debug-text next to the cursor.
Expand Down Expand Up @@ -2035,7 +2043,7 @@ impl Context {
/// Can be used to implement drag-and-drop (see relevant demo).
pub fn translate_layer(&self, layer_id: LayerId, delta: Vec2) {
if delta != Vec2::ZERO {
self.graphics_mut(|g| g.list(layer_id).translate(delta));
self.graphics_mut(|g| g.entry(layer_id).translate(delta));
}
}

Expand Down Expand Up @@ -2346,6 +2354,15 @@ impl Context {
let font_image_size = self.fonts(|f| f.font_image_size());
crate::introspection::font_texture_ui(ui, font_image_size);
});

CollapsingHeader::new("Label text selection state")
.default_open(false)
.show(ui, |ui| {
ui.label(format!(
"{:#?}",
crate::text_selection::LabelSelectionState::load(ui.ctx())
));
});
}

/// Show stats about the allocated textures.
Expand Down
8 changes: 0 additions & 8 deletions crates/egui/src/frame_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,6 @@ pub(crate) struct FrameState {
/// Initialized to `None` at the start of each frame.
pub(crate) tooltip_state: Option<TooltipFrameState>,

/// Set to [`InputState::scroll_delta`] on the start of each frame.
///
/// Cleared by the first [`ScrollArea`] that makes use of it.
pub(crate) scroll_delta: Vec2, // TODO(emilk): move to `InputState` ?

/// horizontal, vertical
pub(crate) scroll_target: [Option<(Rangef, Option<Align>)>; 2],

Expand All @@ -67,7 +62,6 @@ impl Default for FrameState {
unused_rect: Rect::NAN,
used_by_panels: Rect::NAN,
tooltip_state: None,
scroll_delta: Vec2::ZERO,
scroll_target: [None, None],
#[cfg(feature = "accesskit")]
accesskit_state: None,
Expand All @@ -89,7 +83,6 @@ impl FrameState {
unused_rect,
used_by_panels,
tooltip_state,
scroll_delta,
scroll_target,
#[cfg(feature = "accesskit")]
accesskit_state,
Expand All @@ -105,7 +98,6 @@ impl FrameState {
*unused_rect = input.screen_rect();
*used_by_panels = Rect::NOTHING;
*tooltip_state = None;
*scroll_delta = input.scroll_delta;
*scroll_target = [None, None];

#[cfg(debug_assertions)]
Expand Down
74 changes: 65 additions & 9 deletions crates/egui/src/input_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,24 @@ pub struct InputState {
/// (We keep a separate [`TouchState`] for each encountered touch device.)
touch_states: BTreeMap<TouchDeviceId, TouchState>,

/// How many points the user scrolled.
/// Used for smoothing the scroll delta.
unprocessed_scroll_delta: Vec2,

/// The raw input of how many points the user scrolled.
///
/// The delta dictates how the _content_ should move.
///
/// A positive X-value indicates the content is being moved right,
/// as when swiping right on a touch-screen or track-pad with natural scrolling.
///
/// A positive Y-value indicates the content is being moved down,
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
///
/// When using a notched scroll-wheel this will spike very large for one frame,
/// then drop to zero. For a smoother experience, use [`Self::smooth_scroll_delta`].
pub raw_scroll_delta: Vec2,

/// How many points the user scrolled, smoothed over a few frames.
///
/// The delta dictates how the _content_ should move.
///
Expand All @@ -42,7 +59,10 @@ pub struct InputState {
///
/// A positive Y-value indicates the content is being moved down,
/// as when swiping down on a touch-screen or track-pad with natural scrolling.
pub scroll_delta: Vec2,
///
/// [`crate::ScrollArea`] will both read and write to this field, so that
/// at the end of the frame this will be zero if a scroll-area consumed the delta.
pub smooth_scroll_delta: Vec2,

/// Zoom scale factor this frame (e.g. from ctrl-scroll or pinch gesture).
///
Expand Down Expand Up @@ -125,7 +145,9 @@ impl Default for InputState {
raw: Default::default(),
pointer: Default::default(),
touch_states: Default::default(),
scroll_delta: Vec2::ZERO,
unprocessed_scroll_delta: Vec2::ZERO,
raw_scroll_delta: Vec2::ZERO,
smooth_scroll_delta: Vec2::ZERO,
zoom_factor_delta: 1.0,
screen_rect: Rect::from_min_size(Default::default(), vec2(10_000.0, 10_000.0)),
pixels_per_point: 1.0,
Expand Down Expand Up @@ -171,7 +193,7 @@ impl InputState {
let pointer = self.pointer.begin_frame(time, &new);

let mut keys_down = self.keys_down;
let mut scroll_delta = Vec2::ZERO;
let mut raw_scroll_delta = Vec2::ZERO;
let mut zoom_factor_delta = 1.0;
for event in &mut new.events {
match event {
Expand All @@ -189,7 +211,7 @@ impl InputState {
}
}
Event::Scroll(delta) => {
scroll_delta += *delta;
raw_scroll_delta += *delta;
}
Event::Zoom(factor) => {
zoom_factor_delta *= *factor;
Expand All @@ -198,6 +220,22 @@ impl InputState {
}
}

let mut unprocessed_scroll_delta = self.unprocessed_scroll_delta;

let smooth_scroll_delta;

{
// Mouse wheels often go very large steps.
// A single notch on a logitech mouse wheel connected to a Macbook returns 14.0 raw_scroll_delta.
// So we smooth it out over several frames for a nicer user experience when scrolling in egui.
unprocessed_scroll_delta += raw_scroll_delta;
let dt = stable_dt.at_most(0.1);
let t = crate::emath::exponential_smooth_factor(0.90, 0.1, dt); // reach _% in _ seconds. TODO: parameterize

smooth_scroll_delta = t * unprocessed_scroll_delta;
unprocessed_scroll_delta -= smooth_scroll_delta;
}

let mut modifiers = new.modifiers;

let focused_changed = self.focused != new.focused
Expand All @@ -215,7 +253,9 @@ impl InputState {
Self {
pointer,
touch_states: self.touch_states,
scroll_delta,
unprocessed_scroll_delta,
raw_scroll_delta,
smooth_scroll_delta,
zoom_factor_delta,
screen_rect,
pixels_per_point,
Expand Down Expand Up @@ -282,8 +322,11 @@ impl InputState {
)
}

/// The [`crate::Context`] will call this at the end of each frame to see if we need a repaint.
pub fn wants_repaint(&self) -> bool {
self.pointer.wants_repaint() || self.scroll_delta != Vec2::ZERO || !self.events.is_empty()
self.pointer.wants_repaint()
|| self.unprocessed_scroll_delta.abs().max_elem() > 0.2
|| !self.events.is_empty()
}

/// Count presses of a key. If non-zero, the presses are consumed, so that this will only return non-zero once.
Expand Down Expand Up @@ -840,6 +883,7 @@ impl PointerState {
}

/// Was any pointer button pressed (`!down -> down`) this frame?
///
/// This can sometimes return `true` even if `any_down() == false`
/// because a press can be shorted than one frame.
pub fn any_pressed(&self) -> bool {
Expand Down Expand Up @@ -1006,7 +1050,11 @@ impl InputState {
raw,
pointer,
touch_states,
scroll_delta,

unprocessed_scroll_delta,
raw_scroll_delta,
smooth_scroll_delta,

zoom_factor_delta,
screen_rect,
pixels_per_point,
Expand Down Expand Up @@ -1041,7 +1089,15 @@ impl InputState {
});
}

ui.label(format!("scroll_delta: {scroll_delta:?} points"));
if cfg!(debug_assertions) {
ui.label(format!(
"unprocessed_scroll_delta: {unprocessed_scroll_delta:?} points"
));
}
ui.label(format!("raw_scroll_delta: {raw_scroll_delta:?} points"));
ui.label(format!(
"smooth_scroll_delta: {smooth_scroll_delta:?} points"
));
ui.label(format!("zoom_factor_delta: {zoom_factor_delta:4.2}x"));
ui.label(format!("screen_rect: {screen_rect:?} points"));
ui.label(format!(
Expand Down
32 changes: 28 additions & 4 deletions crates/egui/src/layers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ impl LayerId {
}

/// A unique identifier of a specific [`Shape`] in a [`PaintList`].
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ShapeIdx(usize);
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ShapeIdx(pub usize);

/// A list of [`Shape`]s paired with a clip rectangle.
#[derive(Clone, Default)]
Expand Down Expand Up @@ -151,25 +152,48 @@ impl PaintList {
self.0[idx.0] = ClippedShape { clip_rect, shape };
}

/// Set the given shape to be empty (a `Shape::Noop`).
#[inline(always)]
pub fn reset_shape(&mut self, idx: ShapeIdx) {
self.0[idx.0].shape = Shape::Noop;
}

/// Translate each [`Shape`] and clip rectangle by this much, in-place
pub fn translate(&mut self, delta: Vec2) {
for ClippedShape { clip_rect, shape } in &mut self.0 {
*clip_rect = clip_rect.translate(delta);
shape.translate(delta);
}
}

/// Read-only access to all held shapes.
pub fn all_entries(&self) -> impl ExactSizeIterator<Item = &ClippedShape> {
self.0.iter()
}
}

/// This is where painted [`Shape`]s end up during a frame.
#[derive(Clone, Default)]
pub(crate) struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);
pub struct GraphicLayers([IdMap<PaintList>; Order::COUNT]);

impl GraphicLayers {
pub fn list(&mut self, layer_id: LayerId) -> &mut PaintList {
/// Get or insert the [`PaintList`] for the given [`LayerId`].
pub fn entry(&mut self, layer_id: LayerId) -> &mut PaintList {
self.0[layer_id.order as usize]
.entry(layer_id.id)
.or_default()
}

/// Get the [`PaintList`] for the given [`LayerId`].
pub fn get(&self, layer_id: LayerId) -> Option<&PaintList> {
self.0[layer_id.order as usize].get(&layer_id.id)
}

/// Get the [`PaintList`] for the given [`LayerId`].
pub fn get_mut(&mut self, layer_id: LayerId) -> Option<&mut PaintList> {
self.0[layer_id.order as usize].get_mut(&layer_id.id)
}

pub fn drain(&mut self, area_order: &[LayerId]) -> Vec<ClippedShape> {
crate::profile_function!();

Expand Down
Loading

0 comments on commit a367c89

Please sign in to comment.