refactor: split every monolithic module into focused submodules
Each source file that had grown beyond a single concern is replaced by an identically-named directory containing focused submodules. `src/event_loop/mod.rs` (878 lines) becomes a directory with clipboard, context_menu, cursor_shape, drag, focus, handlers, invalidation, overlays_reconcile, repeat, run, surface, text_editing, and tooltip. Every widget, input handler, and theme component follows the same split. Public interfaces are unchanged — only the internal file layout moves. image bumped from 0.25.2 to 0.25.9.
This commit is contained in:
148
src/widget/external/mod.rs
vendored
Normal file
148
src/widget/external/mod.rs
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
//! Widget that hosts content rendered by an external GL producer.
|
||||
//!
|
||||
//! Reserves layout space and, at draw time, samples a caller-provided GL
|
||||
//! texture into the LTK canvas via [`Canvas::draw_external_texture`]. The
|
||||
//! producer (a web engine, a video decoder, …) keeps ownership of the
|
||||
//! texture and updates it on its own cadence; LTK only composites.
|
||||
//!
|
||||
//! # Source closure contract
|
||||
//!
|
||||
//! [`ExternalSource::Texture`] carries an
|
||||
//! `Arc<dyn Fn(&glow::Context, Rect) -> Option<glow::Texture>>` that LTK
|
||||
//! invokes once per frame, with its GLES context current. The producer:
|
||||
//!
|
||||
//! 1. Reads `Rect` (in physical pixels of the host surface) — useful for
|
||||
//! sizing the inner viewport (e.g. resizing a `WPEToplevel` to match)
|
||||
//! and for translating input coordinates by `rect.origin`.
|
||||
//! 2. May allocate / update GL textures against the supplied
|
||||
//! `glow::Context`.
|
||||
//! 3. May bind extension-imported EGLImages onto a persistent texture.
|
||||
//! 4. Returns the `glow::Texture` to sample, or `None` to paint
|
||||
//! transparent (e.g. while the first frame is still being produced).
|
||||
//!
|
||||
//! Returning `None` for one frame and `Some` for the next is fine; LTK
|
||||
//! re-invokes on every redraw.
|
||||
//!
|
||||
//! # Use cases
|
||||
//!
|
||||
//! * `ltk-webkit` hosting a `WPEView`-rendered page.
|
||||
//! * Video / media playback widgets that decode into a GL texture.
|
||||
//! * Any embedding that already has a GLES producer and wants its output
|
||||
//! in-line with the rest of the LTK widget tree.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```rust,no_run
|
||||
//! # use std::sync::{ Arc, Mutex };
|
||||
//! # use ltk::{ External, ExternalSource, Element };
|
||||
//! # #[ derive( Clone ) ] enum Msg {}
|
||||
//! # fn _ex() -> Element<Msg> {
|
||||
//! let cached_texture: Arc<Mutex<Option<glow::Texture>>> = Arc::new( Mutex::new( None ) );
|
||||
//! let cached = Arc::clone( &cached_texture );
|
||||
//! External::new(
|
||||
//! 800.0, 600.0,
|
||||
//! ExternalSource::Texture( Arc::new( move | _gl, _rect | -> Option<glow::Texture>
|
||||
//! {
|
||||
//! *cached.lock().ok()?
|
||||
//! } ) ),
|
||||
//! ).into()
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::render::Canvas;
|
||||
use crate::types::Rect;
|
||||
|
||||
/// A widget that defers its pixels to an external GL texture producer.
|
||||
///
|
||||
/// The widget itself is non-interactive and produces no messages. Wrap it
|
||||
/// in a [`crate::widget::pressable::Pressable`] (or similar) when input
|
||||
/// capture is needed.
|
||||
pub struct External
|
||||
{
|
||||
/// Reserved width in logical pixels.
|
||||
pub width: f32,
|
||||
/// Reserved height in logical pixels.
|
||||
pub height: f32,
|
||||
/// Source of the GL texture sampled at draw time.
|
||||
pub source: ExternalSource,
|
||||
/// Opacity multiplier in `[0.0, 1.0]`.
|
||||
pub opacity: f32,
|
||||
}
|
||||
|
||||
/// Backends an [`External`] widget can pull pixels from.
|
||||
///
|
||||
/// The only variant today is [`ExternalSource::Texture`], a closure
|
||||
/// returning the current GL texture name. Future variants (DMA-BUF
|
||||
/// imports, software pixmaps for the CPU backend, …) can be added
|
||||
/// without changing call sites.
|
||||
#[ derive( Clone ) ]
|
||||
pub enum ExternalSource
|
||||
{
|
||||
/// Closure invoked once per frame with LTK's [`glow::Context`] current
|
||||
/// and the widget's laid-out rect (in physical pixels of the host
|
||||
/// surface). The producer can allocate textures, bind extension-
|
||||
/// imported EGLImages, etc., and returns the texture name to sample.
|
||||
/// Returning `None` paints transparent — useful while the producer
|
||||
/// is still warming up its first frame. The rect is exposed so
|
||||
/// embedders can size their inner viewport to match LTK's actual
|
||||
/// allocation (e.g. resize a WPEToplevel on layout change) and
|
||||
/// translate input coordinates by `rect.origin`.
|
||||
Texture( Arc<dyn Fn( &glow::Context, Rect ) -> Option<glow::Texture> + Send + Sync> ),
|
||||
}
|
||||
|
||||
impl External
|
||||
{
|
||||
/// Build a new external-content widget reserving `width × height`
|
||||
/// logical pixels.
|
||||
pub fn new( width: f32, height: f32, source: ExternalSource ) -> Self
|
||||
{
|
||||
Self { width, height, source, opacity: 1.0 }
|
||||
}
|
||||
|
||||
/// Override the opacity multiplier. Default: `1.0`.
|
||||
pub fn opacity( mut self, opacity: f32 ) -> Self
|
||||
{
|
||||
self.opacity = opacity.clamp( 0.0, 1.0 );
|
||||
self
|
||||
}
|
||||
|
||||
pub fn preferred_size( &self, _max_width: f32 ) -> ( f32, f32 )
|
||||
{
|
||||
( self.width, self.height )
|
||||
}
|
||||
|
||||
pub fn draw( &self, canvas: &mut Canvas, rect: Rect )
|
||||
{
|
||||
// SAFETY-ish: this is the only call site of the source closure;
|
||||
// LTK's draw pass guarantees the GLES context is current and
|
||||
// the canvas is bound to its FBO. The rect we pass is the
|
||||
// widget's laid-out rect in physical pixels — the same
|
||||
// coordinate space pointer events arrive in, so the producer
|
||||
// can use `rect.origin` to translate input.
|
||||
// External content only renders against the GLES backend — the
|
||||
// software backend has no GL texture to sample. Skip silently.
|
||||
let gl = match canvas
|
||||
{
|
||||
Canvas::Software( _ ) => return,
|
||||
Canvas::Gles( c ) => c.gl.clone(),
|
||||
};
|
||||
match &self.source
|
||||
{
|
||||
ExternalSource::Texture( get ) =>
|
||||
{
|
||||
if let Some( tex ) = get( &gl, rect )
|
||||
{
|
||||
canvas.draw_external_texture( tex, rect, self.opacity );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[ cfg( test ) ]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user