pub struct WindowManager { /* private fields */ }Expand description
Manages multiple application windows.
Each window owns its own WidgetTree and PlatformWindow. The manager
routes events by winit WindowId, broadcasts environment changes, and
handles modal dialog blocking.
Implementations§
Source§impl WindowManager
impl WindowManager
pub fn new(theme: Theme) -> Self
Sourcepub fn set_theme_mode(&mut self, mode: ThemeMode)
pub fn set_theme_mode(&mut self, mode: ThemeMode)
Set the theme mode (called by TeksiloAppHandler during initialization).
Sourcepub fn set_event_proxy(&mut self, proxy: AppEventProxy)
pub fn set_event_proxy(&mut self, proxy: AppEventProxy)
Install the event-loop proxy (called by TeksiloAppHandler once the
proxy is available). Enables TitleBarHostCallbacks::request_close
to post CloseWindowRequest back through the event loop.
Sourcepub fn set_app_context_template(&mut self, template: Rc<TreeAppContext>)
pub fn set_app_context_template(&mut self, template: Rc<TreeAppContext>)
Install the per-tree app context template that every newly created window’s WidgetTree should adopt. Called by TeksiloAppHandler when the application registered an event source on the builder.
Sourcepub fn theme_mode(&self) -> ThemeMode
pub fn theme_mode(&self) -> ThemeMode
Get the current theme mode.
Sourcepub fn apply_os_theme(&mut self, os_dark_hint: Option<bool>)
pub fn apply_os_theme(&mut self, os_dark_hint: Option<bool>)
Recompute and broadcast the theme from the current OS appearance,
per the active theme mode. A no-op under Manual. Under Native it
adopts the OS’s actual colours (GNOME/KDE/Cinnamon on Linux); under
FollowSystem it picks the built-in light/dark preset. Both results
carry the id "system".
os_dark_hint is the OS light/dark state as reported by winit (e.g.
from a WindowEvent::ThemeChanged), used as the authoritative source on
platforms where Teksilo’s own OS-colour query is unimplemented
(macOS / Windows, where query_os_theme_colors() returns
NoPreference). On Linux the query reports a real scheme and the hint
is unused. Pass None when no winit signal is available (e.g. a runtime
“follow system” request) — the current window’s reported theme is used
where possible, otherwise it resolves to light.
pub fn set_typesetter(&mut self, typesetter: SharedTypesetter)
Sourcepub fn create_window(
&mut self,
config: WindowConfig,
target: &ActiveEventLoop,
) -> TeksiloWindowId
pub fn create_window( &mut self, config: WindowConfig, target: &ActiveEventLoop, ) -> TeksiloWindowId
Create a new window synchronously. Allocates an id, constructs the winit surface, builds the widget tree, and registers everything in the windows map before returning.
The returned id is immediately usable — it can be passed to
find_window, focus_window, or close_window_by_id, and
state writes through WindowState are applied at the next
drain_window_commands tick.
Sourcepub fn close_window(&mut self, teksilo_id: TeksiloWindowId)
pub fn close_window(&mut self, teksilo_id: TeksiloWindowId)
Close a window by its TeksiloWindowId.
The single choke point every close funnels through:
process_pending calls this both for a
forced close (queue_close /
close_window_by_id) and for a guarded one
(request_close) once its guard has
passed. That makes this the one place to fire
WindowConfig::on_removed
so both paths are covered by a single call site instead of two.
Sourcepub fn queue_close(&mut self, teksilo_id: TeksiloWindowId)
pub fn queue_close(&mut self, teksilo_id: TeksiloWindowId)
Queue an unconditional window closure (processed in the next
event loop tick). The window’s close guard is not consulted —
use this for explicit programmatic closes and framework-internal
teardown (modal dismissals, close_window_by_id). For a
guarded close that a window’s
on_close_requested
can veto, use request_close.
Sourcepub fn request_close(&mut self, teksilo_id: TeksiloWindowId)
pub fn request_close(&mut self, teksilo_id: TeksiloWindowId)
Queue a guarded window closure (processed in the next event
loop tick). Before tearing the window down,
process_pending consults the window’s
close guard (from
WindowConfig::on_close_requested
/ can_close); a
CloseResponse::Veto keeps the
window open. Used for the interactive close gestures: the OS
close button, a custom-chrome close button, and
EventContext::close_window.
Sourcepub fn route_title_bar_synthetic_tap(
&mut self,
teksilo_id: TeksiloWindowId,
target: ControlTarget,
)
pub fn route_title_bar_synthetic_tap( &mut self, teksilo_id: TeksiloWindowId, target: ControlTarget, )
Route a Windows-side synthetic title-bar tap. The wndproc
posts a TitleBarSyntheticEvent when WM_NCLBUTTONUP
fires on a button rect that the OS treated as non-client; we
resolve the matching WidgetId via the host and synthesise a
primary-button tap so the widget’s normal on_tap handler
runs. No-op on platforms that never produce these events.
Sourcepub fn route_title_bar_synthetic_hover(
&mut self,
teksilo_id: TeksiloWindowId,
target: ControlTarget,
entered: bool,
)
pub fn route_title_bar_synthetic_hover( &mut self, teksilo_id: TeksiloWindowId, target: ControlTarget, entered: bool, )
Route a Windows-side synthetic title-bar hover entered/leave.
Delegates to the host’s set_button_hover, which writes the
signal WindowControls registered for the matching button.
No-op on platforms that don’t intercept non-client hover.
Sourcepub fn drain_window_commands(&mut self)
pub fn drain_window_commands(&mut self)
Drain the app→OS command queue on every window and translate
each WindowCommand into the appropriate winit call. Called
once per event-loop tick after event dispatch.
Observers on WindowState signals emit commands when app
code writes through them. OS-originated writes go through the
*_from_os setters on the state, which flip the re-entrancy
guard so the same observers do not fire an echo back out — so
the queue only contains genuine app→OS directives.
Sourcepub fn process_pending(&mut self, target: &ActiveEventLoop)
pub fn process_pending(&mut self, target: &ActiveEventLoop)
Process pending window closures. Called from the event loop
each tick. Creation does not need a drain path — open_window
from handler code goes through WindowOpsImpl and calls
create_window synchronously inside the
same dispatch.
A forced close (queue_close) tears the
window down immediately. A guarded close
(request_close) first runs the window’s
close guard via evaluate_close_guard; a
CloseResponse::Veto keeps the
window open. Guards are strictly per-window, so this is correct
for multi-window apps: each pending close consults only its own
window’s guard.
Sourcepub fn teksilo_id_for_winit(&self, id: WindowId) -> Option<TeksiloWindowId>
pub fn teksilo_id_for_winit(&self, id: WindowId) -> Option<TeksiloWindowId>
Get the TeksiloWindowId for a winit WindowId.
Sourcepub fn find_window(&self, string_id: &str) -> Option<TeksiloWindowId>
pub fn find_window(&self, string_id: &str) -> Option<TeksiloWindowId>
Find a window by its string ID.
Sourcepub fn is_blocked(&self, teksilo_id: TeksiloWindowId) -> bool
pub fn is_blocked(&self, teksilo_id: TeksiloWindowId) -> bool
Whether a window is blocked by a modal child.
pub fn blocking_modal_child( &self, teksilo_id: TeksiloWindowId, ) -> Option<TeksiloWindowId>
pub fn refocus_modal_child(&self, blocked_parent: TeksiloWindowId)
Sourcepub fn set_theme(&mut self, theme: Theme)
pub fn set_theme(&mut self, theme: Theme)
Broadcast a theme change to all windows.
ThemeMode is App-level state, so a user-driven theme set under
FollowSystem/Native is last-writer-wins against the next OS theme
event (handle_theme_changed). The default Manual mode ignores OS
events, so an app that wants user theme choices to stick should stay on
Manual (the default).
Sourcepub fn set_text_scale(&mut self, factor: f32)
pub fn set_text_scale(&mut self, factor: f32)
Broadcast a user text-scale change to all windows. Stores the factor so windows created later inherit it, then re-scales every existing tree’s text without rebuilding.
Sourcepub fn set_initial_text_scale(&mut self, factor: f32)
pub fn set_initial_text_scale(&mut self, factor: f32)
Seed the user text-scale factor before the first window opens. Called by
TeksiloAppHandler after reading teksilo_settings::TEXT_SCALE_KEY, so
every initially-created tree starts at the persisted scale.
Sourcepub fn refresh_accessibility_preferences(&mut self) -> bool
pub fn refresh_accessibility_preferences(&mut self) -> bool
Re-query the OS accessibility preferences (“increase contrast”, “reduce
motion”, text scale) and, if they changed since startup / the last
refresh, apply them to every open window’s tree. Lets a runtime toggle
of these settings take effect without restarting the app (WCAG / EN
301 549 §11.7). Driven event-first — from WindowEvent::Focused when a
window gains focus — so there is no idle polling wakeup. Returns true
if anything changed (so the caller can request a redraw).
Sourcepub fn set_locale(&mut self, locale: LanguageIdentifier)
pub fn set_locale(&mut self, locale: LanguageIdentifier)
Broadcast a locale switch to all windows. Updates the i18n manager
(incrementing the version signal) and seeds each tree with the new
locale and layout direction. No-op if no I18nConfig was registered.
Sourcepub fn accessibility_preferences(&self) -> &AccessibilityPreferences
pub fn accessibility_preferences(&self) -> &AccessibilityPreferences
Get the OS-level accessibility preferences (queried at startup).
Sourcepub fn primary_window_id(&self) -> TeksiloWindowId
pub fn primary_window_id(&self) -> TeksiloWindowId
Get the TeksiloWindowId of the first (primary) window. Falls back to a synthetic ID when no windows are open yet.
Sourcepub fn window_count(&self) -> usize
pub fn window_count(&self) -> usize
Number of active windows.
Sourcepub fn winit_id_for_teksilo(
&self,
teksilo_id: TeksiloWindowId,
) -> Option<WindowId>
pub fn winit_id_for_teksilo( &self, teksilo_id: TeksiloWindowId, ) -> Option<WindowId>
Get the winit WindowId for a TeksiloWindowId.
Sourcepub fn title_bar_host(
&self,
teksilo_id: TeksiloWindowId,
) -> Option<Rc<dyn PlatformTitleBarHost>>
pub fn title_bar_host( &self, teksilo_id: TeksiloWindowId, ) -> Option<Rc<dyn PlatformTitleBarHost>>
Get the platform title bar host for a window, if the window opted
into custom chrome via WindowConfig::custom_chrome(true) and the
platform supports it. Returns None for windows that use native
decorations or run on a window system without custom chrome support
(currently X11).
Sourcepub fn request_redraw_all(&self)
pub fn request_redraw_all(&self)
Request redraw on all windows.
Sourcepub fn request_redraw_due(&self, now: Instant)
pub fn request_redraw_due(&self, now: Instant)
Request redraw only on windows whose next frame deadline has been
reached (<= now). Used at the animation ResumeTimeReached wake so
that a single animating window (e.g. a blinking caret) does NOT force a
redraw of every other window.
The blanket request_redraw_all() here was a cross-window over-redraw:
a wasted-power bug on every platform (an inactive, non-animating window
repainted at 60 Hz), and on Windows a correctness bug — winit services
only one window’s RedrawRequested per event-loop iteration, so an
inactive window flooded with redraws it never wins is starved of its own
pending repaint and freezes on its last active frame (caret stuck,
colours not desaturated). Targeting only due windows removes both.
Sourcepub fn request_redraw_needing_render(&mut self) -> usize
pub fn request_redraw_needing_render(&mut self) -> usize
Reconcile every window’s reactive (Signal-bound) state and
request redraw ONLY on the ones that come out of that with pending
layout or paint work (WidgetTree::needs_render). Returns how many
windows were poked, purely so a caller can log/trace it.
§The problem
A Signal mutation made by a handler in one window’s dispatch
(e.g. writing to an app-level Signal a sibling window’s widget
also reads) is supposed to make that sibling dirty too — that’s the
whole point of sharing a Signal across windows. But only the
dispatching window’s own event-handling path calls
request_redraw() on itself (see the WindowEvent::CursorMoved /
MouseInput / KeyboardInput arms); nothing tells winit to
schedule a RedrawRequested for the sibling, so it shows a stale
frame until the user focuses it (which finally earns it a paint).
§Why this can’t just check needs_render()
A Signal write only advances a change generation on the signal
itself (a deliberately lazy design — a signal has no reference
back into any WidgetTree’s arena to mark node-level dirty bits
synchronously). That generation is only compared against what
each window’s BindingRegistry last acted on, and walked into
arena.needs_layout / needs_paint — i.e. into what
needs_render() actually reads — by WidgetTree’s internal
process_state_changes step, which today runs only at the top
of that tree’s own layout(). A window’s own
layout() runs only as part of handling its own
RedrawRequested (see handle_redraw_requested in app.rs). So a
sibling window that never redraws never reconciles its bindings
either — its needs_render() reads false forever, not just
“until it happens to repaint”, because nothing ever performed the
walk that would make it true. Checking needs_render() without
reconciling first would make this method a permanent no-op for
exactly the case it exists to fix.
That’s why a window’s tree.layout() is called here first, at
its OWN current size (proposal_changed stays false), whenever
WidgetTree::needs_reconcile says reconciling could change the
answer. This is the only place in the framework doing this
specific reconciliation for a plain app-level Signal (the
theme/locale/text-scale/follow-system broadcasts sidestep the
whole problem by mutating each tree directly through
&mut self.windows, not through a shared Signal, so they
always know synchronously that every window needs a redraw).
§Why the reconcile is gated rather than unconditional
It was unconditional at first, on the grounds that layout()
short-circuits before the per-node geometry recursion when
nothing is dirty. That undersells the cost: layout_with_ops
runs a dozen per-frame passes before it gets near that
short-circuit — pending animations, the frame tick, the animation
scheduler tick, drag ticks, the whole of process_state_changes,
tooltips, delayed / pointer-leave / auto-dismiss overlays and
overlay fades — and it ran all of them for every open window on
every dispatched event, including a fast mouse-move stream.
needs_reconcile() is cheap enough to ask instead (u64
comparisons over unique bound sources, no arena walk) and answers
precisely the question this sweep is for. It is safe to skip the
rest because everything in that list has its own scheduling path
— the timing-driven passes all feed WidgetTree::next_timer_deadline,
which request_redraw_due polls — see needs_reconcile’s own doc
for the case-by-case argument.
The needs_render() check stays OUTSIDE the gate on purpose: a
window can need a repaint for reasons that never involved a
binding (a handler called request_rebuild, an event dirtied a
node directly), and skipping the reconcile must not also skip
poking it.
§Reconciling here does not rob the window of its own reconcile
Load-bearing, and it was not always true. Dirty tracking used to
be a bool living on the Signal, which the first registry to
flush both read AND cleared — so this very sweep was the thing
that broke shared-Signal fan-out: whichever window it happened
to visit first consumed the change and every later window’s
reconcile found nothing, permanently. Signal now exposes a
monotone generation and each BindingRegistry remembers what it
last acted on (see teksilo_core::binding::BindingGroup), so
visiting windows in any order — this method iterates a HashMap,
so the order is arbitrary and varies run to run — delivers the
change to all of them.
One known limitation: this uses WidgetTree::layout (a
NoopWindowOps sink), not layout_with_ops, so a handler that
this reconcile pass happens to run (a data-driven rebuild reacting
to the very state change being reconciled) cannot synchronously
open a window from here. That is acceptable for a background
reconciliation pass — the window still opens correctly the next
time this sibling performs its own real redraw with a real
WindowOps sink, exactly the way an app that never called this
method at all would have behaved.
§Why needs_render(), not needs_redraw()
Deliberately narrower than the broader needs_redraw(), which
also reports true while a per-frame shader animation is merely
running, with no dirty paint or layout at all. Treating “an
animation is running” as a reason to force an extra redraw here —
on top of whatever other window’s event just got dispatched —
would effectively re-couple that animating window’s frame rate to
the dispatch rate of every OTHER window’s input (a fast mouse-move
stream can exceed 60 Hz), defeating the 60 Hz WaitUntil pacing
those animations already get from next_timer_deadline /
request_redraw_due and reintroducing the exact uncapped
free-running redraw behaviour that pacing was written to remove.
Sourcepub fn drain_pending_modal_requests(
&mut self,
) -> Vec<(TeksiloWindowId, Vec<QueuedModalRequest>)>
pub fn drain_pending_modal_requests( &mut self, ) -> Vec<(TeksiloWindowId, Vec<QueuedModalRequest>)>
Drain pending modal requests from all windows.
Sourcepub fn drain_pending_modal_dismissals(&mut self) -> Vec<TeksiloWindowId>
pub fn drain_pending_modal_dismissals(&mut self) -> Vec<TeksiloWindowId>
Drain native modal-window dismiss requests from all windows.
Sourcepub fn drain_close_window_requests(&mut self) -> bool
pub fn drain_close_window_requests(&mut self) -> bool
Drain per-tree close-window requests raised by handlers via
EventContext::close_window
(a guarded close) and
EventContext::close_window_forced
(a forced close that bypasses the window’s close guard).
Returns true when at least one window was queued for closing.
A forced request wins over a guarded one for the same window in the same drain — if a handler called both, the window closes unconditionally.
Sourcepub fn drain_pending_locale_requests(&mut self) -> bool
pub fn drain_pending_locale_requests(&mut self) -> bool
Drain per-tree locale-switch requests raised by handlers via
EventContext::set_locale,
parse each one to a LanguageIdentifier, and route it through
WindowManager::set_locale so the I18nManager (active locale,
version signal, RTL direction) and every tree stay in sync.
Invalid or unsupported locale strings are logged and dropped.
Returns true if any request was drained, so the caller can repaint
every window — the fan-out marks non-originating windows dirty but
only the window that received the triggering event gets its own
request_redraw.
Sourcepub fn drain_pending_theme_requests(&mut self) -> bool
pub fn drain_pending_theme_requests(&mut self) -> bool
Drain per-tree theme-switch requests raised by handlers via
EventContext::set_theme
and route each through WindowManager::set_theme so the new theme is
applied to every window, not just the one whose handler requested it.
Returns true if any request was drained (same repaint rationale as
WindowManager::drain_pending_locale_requests). If several windows
raised a request in the same tick, each is applied in turn — the last
wins, matching the locale path.
Sourcepub fn drain_pending_follow_system_requests(&mut self) -> bool
pub fn drain_pending_follow_system_requests(&mut self) -> bool
Drain per-tree “follow OS theme” requests raised by handlers via
EventContext::follow_system_theme.
Switches the app to ThemeMode::Native and recomputes the theme from
the current OS colours, fanning it to every window. Returns true if
any window requested it (so the caller schedules a repaint).
Sourcepub fn drain_pending_text_scale_requests(&mut self) -> bool
pub fn drain_pending_text_scale_requests(&mut self) -> bool
Drain per-tree text-scale requests raised by handlers via
EventContext::set_text_scale
and route each through WindowManager::set_text_scale so the new
factor is applied to every window. Returns true if any request was
drained (so the caller schedules a repaint). Last writer wins if several
windows requested in the same tick.
Auto Trait Implementations§
impl !RefUnwindSafe for WindowManager
impl !Send for WindowManager
impl !Sync for WindowManager
impl !UnwindSafe for WindowManager
impl Freeze for WindowManager
impl Unpin for WindowManager
impl UnsafeUnpin for WindowManager
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more