Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,3 @@ rfd-async-std = ["dep:rfd", "rfd/async-std"]
rfd-tokio = ["dep:rfd", "rfd/tokio"]
crossbeam = ["dep:crossbeam", "floem_renderer/crossbeam"]
localization = ["dep:fluent-bundle", "dep:unic-langid", "dep:sys-locale"]

[profile.dev]
opt-level = 1


5 changes: 5 additions & 0 deletions reactive/src/signal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ impl<T> PartialEq for RwSignal<T> {
self.id == other.id
}
}
impl<T> std::hash::Hash for RwSignal<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}

impl<T> fmt::Debug for RwSignal<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
2 changes: 1 addition & 1 deletion src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ impl ApplicationHandler for Application {
for event in self.receiver.try_iter() {
self.handle.handle_user_event(event_loop, event);
}
self.handle.handle_updates_for_all_windows();
self.handle.handle_updates_for_all_windows(true);
}

fn destroy_surfaces(&mut self, _event_loop: &dyn ActiveEventLoop) {
Expand Down
54 changes: 31 additions & 23 deletions src/app_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,7 @@ impl ApplicationHandle {
WindowEvent::DragLeft { .. } => "DragLeft",
WindowEvent::DragMoved { .. } => "DragMoved",
};
(
name,
Instant::now(),
matches!(event, WindowEvent::RedrawRequested),
)
(name, Instant::now())
});

match window_handle
Expand All @@ -222,6 +218,8 @@ impl ApplicationHandle {
None => {}
}

let mut rendered = false;

match event {
WindowEvent::ActivationTokenDone { .. } => {}
WindowEvent::SurfaceResized(size) => {
Expand Down Expand Up @@ -270,9 +268,6 @@ impl ApplicationHandle {
WindowEvent::Focused(focused) => {
window_handle.focused(focused);
}
WindowEvent::KeyboardInput { .. } => {
// already handled by the ui-events reducer
}
WindowEvent::ModifiersChanged(modifiers) => {
window_handle.modifiers_changed(
ui_events_winit::keyboard::from_winit_modifier_state(modifiers.state()),
Expand All @@ -281,11 +276,6 @@ impl ApplicationHandle {
WindowEvent::Ime(ime) => {
window_handle.ime(ime);
}
WindowEvent::MouseWheel { .. } => {}
WindowEvent::PinchGesture {
delta: _, phase: _, ..
} => {}
WindowEvent::TouchpadPressure { .. } => {}
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
window_handle.scale(scale_factor);
}
Expand All @@ -294,8 +284,12 @@ impl ApplicationHandle {
}
WindowEvent::Occluded(_) => {}
WindowEvent::RedrawRequested => {
window_handle.render_frame(self.gpu_resources.clone());
rendered = window_handle.render_frame(self.gpu_resources.clone());
}
WindowEvent::KeyboardInput { .. } => {
// already handled by the ui-events reducer
}
WindowEvent::TouchpadPressure { .. } => {}
WindowEvent::PanGesture { .. } => {}
WindowEvent::DoubleTapGesture { .. } => {}
WindowEvent::RotationGesture { .. } => {}
Expand All @@ -311,25 +305,27 @@ impl ApplicationHandle {
WindowEvent::PointerButton { .. } => {
//already handled by the ui-events reducer
}
WindowEvent::MouseWheel { .. } => {}
WindowEvent::PinchGesture { .. } => {}
}

if let Some((name, start, new_frame)) = start {
if let Some((name, start)) = start {
let end = Instant::now();

if let Some(window_handle) = self.window_handles.get_mut(&window_id) {
let profile = window_handle.profile.as_mut().unwrap();

profile
.current
.events
.push(ProfileEvent { start, end, name });

if new_frame {
if rendered {
profile.next_frame();
}
}
}
self.handle_updates_for_all_windows();
// we don't handle updates for the window here and instead leave them to be processed just before drawing a frame.
// if we don't do this, we can get into a never ending loop where we keep processing input events and never actually drawing a frame
self.handle_updates_for_all_windows(false);
}

pub(crate) fn new_window(
Expand Down Expand Up @@ -554,18 +550,30 @@ impl ApplicationHandle {
}

pub(crate) fn idle(&mut self) {
let start = Instant::now();
let ext_events = { std::mem::take(&mut *EXT_EVENT_HANDLER.queue.lock()) };

for trigger in ext_events {
trigger.notify();
}

self.handle_updates_for_all_windows();
self.handle_updates_for_all_windows(true);
for window in self.window_handles.values_mut() {
if let Some(profile) = &mut window.profile {
profile.current.events.push(ProfileEvent {
start,
end: Instant::now(),
name: "Idle processing",
});
}
}
}

pub(crate) fn handle_updates_for_all_windows(&mut self) {
pub(crate) fn handle_updates_for_all_windows(&mut self, process_handle: bool) {
for (window_id, handle) in self.window_handles.iter_mut() {
handle.process_update();
if process_handle {
handle.process_update();
}
while process_window_updates(window_id) {}
}
}
Expand Down Expand Up @@ -613,7 +621,7 @@ impl ApplicationHandle {
(timer.action)(token);
}
}
self.handle_updates_for_all_windows();
self.handle_updates_for_all_windows(true);
}
self.fire_timer(event_loop);
}
Expand Down
36 changes: 15 additions & 21 deletions src/ext_event.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use std::{cell::Cell, collections::VecDeque, sync::Arc};
use std::{
cell::Cell,
collections::VecDeque,
sync::{Arc, LazyLock},
};

use floem_reactive::{
ReadSignal, RwSignal, Scope, SignalGet, SignalUpdate, SignalWith, WriteSignal, create_effect,
create_rw_signal, untrack, with_scope,
};
use indexmap::IndexSet;
use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

use crate::{
Expand All @@ -21,7 +26,7 @@ use std::sync::mpsc::Receiver;
///
/// **DO NOT USE THIS** trigger except for when using with `create_ext_action` or when you guarantee that
/// the signal is never used from a different thread than it was created on.
#[derive(Debug)]
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ExtSendTrigger {
signal: RwSignal<()>,
}
Expand Down Expand Up @@ -58,32 +63,21 @@ pub fn create_trigger() -> ExtSendTrigger {
unsafe impl Send for ExtSendTrigger {}
unsafe impl Sync for ExtSendTrigger {}

pub(crate) static EXT_EVENT_HANDLER: ExtEventHandler = ExtEventHandler::new();
pub(crate) static EXT_EVENT_HANDLER: LazyLock<ExtEventHandler> =
LazyLock::new(|| ExtEventHandler {
queue: Mutex::new(IndexSet::new()),
});

pub(crate) struct ExtEventHandler {
pub(crate) queue: Mutex<VecDeque<ExtSendTrigger>>,
}

impl Default for ExtEventHandler {
fn default() -> Self {
Self::new()
}
pub(crate) queue: Mutex<IndexSet<ExtSendTrigger>>,
}

impl ExtEventHandler {
pub const fn new() -> Self {
Self {
queue: Mutex::new(VecDeque::new()),
}
}

pub fn add_trigger(&self, trigger: ExtSendTrigger) {
{
// Run this in a short block to prevent any deadlock if running the trigger effects
// causes another trigger to be registered
EXT_EVENT_HANDLER.queue.lock().push_back(trigger);
let inserted = self.queue.lock().insert(trigger);
if inserted {
Application::send_proxy_event(UserEvent::Idle);
}
Application::send_proxy_event(UserEvent::Idle);
}
}

Expand Down
27 changes: 17 additions & 10 deletions src/window_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,8 @@ impl WindowHandle {
}
}

self.process_update();
// Schedule update processing - render_frame will determine if paint is needed
self.schedule_repaint();
}

pub(crate) fn scale(&mut self, scale: f64) {
Expand Down Expand Up @@ -512,9 +513,9 @@ impl WindowHandle {
self.event(Event::WindowMaximizeChanged(is_maximized));
}

// For resize events, we need immediate style/layout since window geometry changed
self.style();
self.layout();
self.process_update();
self.schedule_repaint();
}

Expand Down Expand Up @@ -568,7 +569,8 @@ impl WindowHandle {
id.request_style();
}
}
self.process_update();
// Schedule update processing for pointer leave events
self.schedule_repaint();
}
_ => {}
}
Expand Down Expand Up @@ -621,7 +623,7 @@ impl WindowHandle {
cx.compute_view_layout(self.id);
}

pub(crate) fn render_frame(&mut self, gpu_resources: Option<GpuResources>) {
pub(crate) fn render_frame(&mut self, gpu_resources: Option<GpuResources>) -> bool {
// Processes updates scheduled on this frame.
for update in mem::take(&mut self.window_state.scheduled_updates) {
match update {
Expand All @@ -631,13 +633,18 @@ impl WindowHandle {
}
}

self.process_update_no_paint();
self.paint(gpu_resources);
let needs_paint = self.process_update_no_paint();

// Only paint if there were actual visual changes
if needs_paint {
self.paint(gpu_resources);
}

// Request a new frame if there's any scheduled updates.
if !self.window_state.scheduled_updates.is_empty() {
self.schedule_repaint();
}
needs_paint
}

pub fn paint(&mut self, gpu_resources: Option<GpuResources>) -> Option<peniko::ImageBrush> {
Expand Down Expand Up @@ -1085,15 +1092,15 @@ impl WindowHandle {
}
}

fn needs_layout(&mut self) -> bool {
fn needs_layout(&self) -> bool {
self.id
.state()
.borrow()
.requested_changes
.contains(ChangeFlags::LAYOUT)
}

fn needs_style(&mut self) -> bool {
fn needs_style(&self) -> bool {
self.id
.state()
.borrow()
Expand Down Expand Up @@ -1247,10 +1254,10 @@ impl WindowHandle {
set_current_view(self.id);
if let Some(action) = self.window_state.context_menu.get(id) {
(*action)();
self.process_update();
self.schedule_repaint();
} else if let Some(action) = self.window_menu_actions.get(id) {
(*action)();
self.process_update();
self.schedule_repaint();
}
}

Expand Down