ul_next/
view.rs

1//! The View is a component used to load and display web content.
2use std::{ops::Deref, sync::Arc};
3
4use crate::{
5    bitmap::BitmapFormat,
6    error::CreationError,
7    event::{KeyEvent, MouseEvent, ScrollEvent},
8    javascript::JSContext,
9    rect::Rect,
10    renderer::Session,
11    string::UlString,
12    surface::Surface,
13    Library,
14};
15
16/// Cursor types (See [`View::set_change_cursor_callback`] and [`Window::set_cursor`][crate::window::Window::set_cursor])
17#[derive(Clone, Copy, Debug)]
18pub enum Cursor {
19    Alias = ul_sys::ULCursor_kCursor_Alias as isize,
20    Cell = ul_sys::ULCursor_kCursor_Cell as isize,
21    ColumnResize = ul_sys::ULCursor_kCursor_ColumnResize as isize,
22    ContextMenu = ul_sys::ULCursor_kCursor_ContextMenu as isize,
23    Copy = ul_sys::ULCursor_kCursor_Copy as isize,
24    Cross = ul_sys::ULCursor_kCursor_Cross as isize,
25    Custom = ul_sys::ULCursor_kCursor_Custom as isize,
26    EastPanning = ul_sys::ULCursor_kCursor_EastPanning as isize,
27    EastResize = ul_sys::ULCursor_kCursor_EastResize as isize,
28    EastWestResize = ul_sys::ULCursor_kCursor_EastWestResize as isize,
29    Grab = ul_sys::ULCursor_kCursor_Grab as isize,
30    Grabbing = ul_sys::ULCursor_kCursor_Grabbing as isize,
31    Hand = ul_sys::ULCursor_kCursor_Hand as isize,
32    Help = ul_sys::ULCursor_kCursor_Help as isize,
33    IBeam = ul_sys::ULCursor_kCursor_IBeam as isize,
34    MiddlePanning = ul_sys::ULCursor_kCursor_MiddlePanning as isize,
35    Move = ul_sys::ULCursor_kCursor_Move as isize,
36    NoDrop = ul_sys::ULCursor_kCursor_NoDrop as isize,
37    None = ul_sys::ULCursor_kCursor_None as isize,
38    NorthEastPanning = ul_sys::ULCursor_kCursor_NorthEastPanning as isize,
39    NorthEastResize = ul_sys::ULCursor_kCursor_NorthEastResize as isize,
40    NorthEastSouthWestResize = ul_sys::ULCursor_kCursor_NorthEastSouthWestResize as isize,
41    NorthPanning = ul_sys::ULCursor_kCursor_NorthPanning as isize,
42    NorthResize = ul_sys::ULCursor_kCursor_NorthResize as isize,
43    NorthSouthResize = ul_sys::ULCursor_kCursor_NorthSouthResize as isize,
44    NorthWestPanning = ul_sys::ULCursor_kCursor_NorthWestPanning as isize,
45    NorthWestResize = ul_sys::ULCursor_kCursor_NorthWestResize as isize,
46    NorthWestSouthEastResize = ul_sys::ULCursor_kCursor_NorthWestSouthEastResize as isize,
47    NotAllowed = ul_sys::ULCursor_kCursor_NotAllowed as isize,
48    Pointer = ul_sys::ULCursor_kCursor_Pointer as isize,
49    Progress = ul_sys::ULCursor_kCursor_Progress as isize,
50    RowResize = ul_sys::ULCursor_kCursor_RowResize as isize,
51    SouthEastPanning = ul_sys::ULCursor_kCursor_SouthEastPanning as isize,
52    SouthEastResize = ul_sys::ULCursor_kCursor_SouthEastResize as isize,
53    SouthPanning = ul_sys::ULCursor_kCursor_SouthPanning as isize,
54    SouthResize = ul_sys::ULCursor_kCursor_SouthResize as isize,
55    SouthWestPanning = ul_sys::ULCursor_kCursor_SouthWestPanning as isize,
56    SouthWestResize = ul_sys::ULCursor_kCursor_SouthWestResize as isize,
57    VerticalText = ul_sys::ULCursor_kCursor_VerticalText as isize,
58    Wait = ul_sys::ULCursor_kCursor_Wait as isize,
59    WestPanning = ul_sys::ULCursor_kCursor_WestPanning as isize,
60    WestResize = ul_sys::ULCursor_kCursor_WestResize as isize,
61    ZoomIn = ul_sys::ULCursor_kCursor_ZoomIn as isize,
62    ZoomOut = ul_sys::ULCursor_kCursor_ZoomOut as isize,
63}
64
65impl TryFrom<ul_sys::ULCursor> for Cursor {
66    type Error = ();
67
68    fn try_from(value: ul_sys::ULCursor) -> Result<Self, Self::Error> {
69        match value {
70            ul_sys::ULCursor_kCursor_Alias => Ok(Self::Alias),
71            ul_sys::ULCursor_kCursor_Cell => Ok(Self::Cell),
72            ul_sys::ULCursor_kCursor_ColumnResize => Ok(Self::ColumnResize),
73            ul_sys::ULCursor_kCursor_ContextMenu => Ok(Self::ContextMenu),
74            ul_sys::ULCursor_kCursor_Copy => Ok(Self::Copy),
75            ul_sys::ULCursor_kCursor_Cross => Ok(Self::Cross),
76            ul_sys::ULCursor_kCursor_Custom => Ok(Self::Custom),
77            ul_sys::ULCursor_kCursor_EastPanning => Ok(Self::EastPanning),
78            ul_sys::ULCursor_kCursor_EastResize => Ok(Self::EastResize),
79            ul_sys::ULCursor_kCursor_EastWestResize => Ok(Self::EastWestResize),
80            ul_sys::ULCursor_kCursor_Grab => Ok(Self::Grab),
81            ul_sys::ULCursor_kCursor_Grabbing => Ok(Self::Grabbing),
82            ul_sys::ULCursor_kCursor_Hand => Ok(Self::Hand),
83            ul_sys::ULCursor_kCursor_Help => Ok(Self::Help),
84            ul_sys::ULCursor_kCursor_IBeam => Ok(Self::IBeam),
85            ul_sys::ULCursor_kCursor_MiddlePanning => Ok(Self::MiddlePanning),
86            ul_sys::ULCursor_kCursor_Move => Ok(Self::Move),
87            ul_sys::ULCursor_kCursor_NoDrop => Ok(Self::NoDrop),
88            ul_sys::ULCursor_kCursor_None => Ok(Self::None),
89            ul_sys::ULCursor_kCursor_NorthEastPanning => Ok(Self::NorthEastPanning),
90            ul_sys::ULCursor_kCursor_NorthEastResize => Ok(Self::NorthEastResize),
91            ul_sys::ULCursor_kCursor_NorthEastSouthWestResize => Ok(Self::NorthEastSouthWestResize),
92            ul_sys::ULCursor_kCursor_NorthPanning => Ok(Self::NorthPanning),
93            ul_sys::ULCursor_kCursor_NorthResize => Ok(Self::NorthResize),
94            ul_sys::ULCursor_kCursor_NorthSouthResize => Ok(Self::NorthSouthResize),
95            ul_sys::ULCursor_kCursor_NorthWestPanning => Ok(Self::NorthWestPanning),
96            ul_sys::ULCursor_kCursor_NorthWestResize => Ok(Self::NorthWestResize),
97            ul_sys::ULCursor_kCursor_NorthWestSouthEastResize => Ok(Self::NorthWestSouthEastResize),
98            ul_sys::ULCursor_kCursor_NotAllowed => Ok(Self::NotAllowed),
99            ul_sys::ULCursor_kCursor_Pointer => Ok(Self::Pointer),
100            ul_sys::ULCursor_kCursor_Progress => Ok(Self::Progress),
101            ul_sys::ULCursor_kCursor_RowResize => Ok(Self::RowResize),
102            ul_sys::ULCursor_kCursor_SouthEastPanning => Ok(Self::SouthEastPanning),
103            ul_sys::ULCursor_kCursor_SouthEastResize => Ok(Self::SouthEastResize),
104            ul_sys::ULCursor_kCursor_SouthPanning => Ok(Self::SouthPanning),
105            ul_sys::ULCursor_kCursor_SouthResize => Ok(Self::SouthResize),
106            ul_sys::ULCursor_kCursor_SouthWestPanning => Ok(Self::SouthWestPanning),
107            ul_sys::ULCursor_kCursor_SouthWestResize => Ok(Self::SouthWestResize),
108            ul_sys::ULCursor_kCursor_VerticalText => Ok(Self::VerticalText),
109            ul_sys::ULCursor_kCursor_Wait => Ok(Self::Wait),
110            ul_sys::ULCursor_kCursor_WestPanning => Ok(Self::WestPanning),
111            ul_sys::ULCursor_kCursor_WestResize => Ok(Self::WestResize),
112            ul_sys::ULCursor_kCursor_ZoomIn => Ok(Self::ZoomIn),
113            ul_sys::ULCursor_kCursor_ZoomOut => Ok(Self::ZoomOut),
114            _ => Err(()),
115        }
116    }
117}
118
119/// Rendering details for a View, to be used with your own GPUDriver
120///
121/// When using your own [`GpuDriver`](crate::gpu_driver::GpuDriver), each
122/// [`View`] is rendered to an offscreen texture that you can
123/// display on a 3D quad in your application. This struct provides all the
124/// details you need to display the corresponding texture in your application.
125#[derive(Clone, Copy, Debug)]
126pub struct RenderTarget {
127    /// Whether this target is empty (null texture)
128    pub is_empty: bool,
129    /// The viewport width (in device coordinates).
130    pub width: u32,
131    /// The viewport height (in device coordinates).
132    pub height: u32,
133    /// The GPUDriver-specific texture ID (the texture will be created
134    /// in [`GpuDriver::create_texture`](crate::gpu_driver::GpuDriver::create_texture)).
135    pub texture_id: u32,
136    /// The texture width (in pixels). This may be padded.
137    pub texture_width: u32,
138    /// The texture height (in pixels). This may be padded.
139    pub texture_height: u32,
140    /// The pixel format of the texture.
141    pub texture_format: BitmapFormat,
142    /// UV coordinates of the texture (this is needed because the texture may be padded).
143    pub uv_coords: Rect<f32>,
144    /// The GPUDriver-specific render buffer ID (the render buffer will be created
145    /// in [`GpuDriver::create_render_buffer`](crate::gpu_driver::GpuDriver::create_render_buffer)).
146    pub render_buffer_id: u32,
147}
148
149impl From<ul_sys::ULRenderTarget> for RenderTarget {
150    fn from(rt: ul_sys::ULRenderTarget) -> Self {
151        RenderTarget {
152            is_empty: rt.is_empty,
153            width: rt.width,
154            height: rt.height,
155            texture_id: rt.texture_id,
156            texture_width: rt.texture_width,
157            texture_height: rt.texture_height,
158            texture_format: BitmapFormat::try_from(rt.texture_format).unwrap(),
159            uv_coords: rt.uv_coords.into(),
160            render_buffer_id: rt.render_buffer_id,
161        }
162    }
163}
164
165/// Console message source types (See [`View::set_add_console_message_callback`])
166#[derive(Clone, Copy, Debug)]
167#[non_exhaustive]
168pub enum ConsoleMessageSource {
169    XML = ul_sys::ULMessageSource_kMessageSource_XML as isize,
170    JS = ul_sys::ULMessageSource_kMessageSource_JS as isize,
171    Network = ul_sys::ULMessageSource_kMessageSource_Network as isize,
172    ConsoleAPI = ul_sys::ULMessageSource_kMessageSource_ConsoleAPI as isize,
173    Storage = ul_sys::ULMessageSource_kMessageSource_Storage as isize,
174    AppCache = ul_sys::ULMessageSource_kMessageSource_AppCache as isize,
175    Rendering = ul_sys::ULMessageSource_kMessageSource_Rendering as isize,
176    CSS = ul_sys::ULMessageSource_kMessageSource_CSS as isize,
177    Security = ul_sys::ULMessageSource_kMessageSource_Security as isize,
178    ContentBlocker = ul_sys::ULMessageSource_kMessageSource_ContentBlocker as isize,
179    Media = ul_sys::ULMessageSource_kMessageSource_Media as isize,
180    MediaSource = ul_sys::ULMessageSource_kMessageSource_MediaSource as isize,
181    WebRTC = ul_sys::ULMessageSource_kMessageSource_WebRTC as isize,
182    ITPDebug = ul_sys::ULMessageSource_kMessageSource_ITPDebug as isize,
183    PrivateClickMeasurement =
184        ul_sys::ULMessageSource_kMessageSource_PrivateClickMeasurement as isize,
185    PaymentRequest = ul_sys::ULMessageSource_kMessageSource_PaymentRequest as isize,
186    Other = ul_sys::ULMessageSource_kMessageSource_Other as isize,
187}
188
189impl TryFrom<ul_sys::ULMessageSource> for ConsoleMessageSource {
190    type Error = ();
191    fn try_from(value: ul_sys::ULMessageSource) -> Result<Self, Self::Error> {
192        match value {
193            ul_sys::ULMessageSource_kMessageSource_XML => Ok(ConsoleMessageSource::XML),
194            ul_sys::ULMessageSource_kMessageSource_JS => Ok(ConsoleMessageSource::JS),
195            ul_sys::ULMessageSource_kMessageSource_Network => Ok(ConsoleMessageSource::Network),
196            ul_sys::ULMessageSource_kMessageSource_ConsoleAPI => {
197                Ok(ConsoleMessageSource::ConsoleAPI)
198            }
199            ul_sys::ULMessageSource_kMessageSource_Storage => Ok(ConsoleMessageSource::Storage),
200            ul_sys::ULMessageSource_kMessageSource_AppCache => Ok(ConsoleMessageSource::AppCache),
201            ul_sys::ULMessageSource_kMessageSource_Rendering => Ok(ConsoleMessageSource::Rendering),
202            ul_sys::ULMessageSource_kMessageSource_CSS => Ok(ConsoleMessageSource::CSS),
203            ul_sys::ULMessageSource_kMessageSource_Security => Ok(ConsoleMessageSource::Security),
204            ul_sys::ULMessageSource_kMessageSource_ContentBlocker => {
205                Ok(ConsoleMessageSource::ContentBlocker)
206            }
207            ul_sys::ULMessageSource_kMessageSource_Other => Ok(ConsoleMessageSource::Other),
208            _ => Err(()),
209        }
210    }
211}
212
213/// Console message levels (See [`View::set_add_console_message_callback`])
214#[derive(Clone, Copy, Debug)]
215pub enum ConsoleMessageLevel {
216    Log = ul_sys::ULMessageLevel_kMessageLevel_Log as isize,
217    Warning = ul_sys::ULMessageLevel_kMessageLevel_Warning as isize,
218    Error = ul_sys::ULMessageLevel_kMessageLevel_Error as isize,
219    Debug = ul_sys::ULMessageLevel_kMessageLevel_Debug as isize,
220    Info = ul_sys::ULMessageLevel_kMessageLevel_Info as isize,
221}
222
223impl TryFrom<ul_sys::ULMessageLevel> for ConsoleMessageLevel {
224    type Error = ();
225    fn try_from(value: ul_sys::ULMessageLevel) -> Result<Self, ()> {
226        match value {
227            ul_sys::ULMessageLevel_kMessageLevel_Log => Ok(ConsoleMessageLevel::Log),
228            ul_sys::ULMessageLevel_kMessageLevel_Warning => Ok(ConsoleMessageLevel::Warning),
229            ul_sys::ULMessageLevel_kMessageLevel_Error => Ok(ConsoleMessageLevel::Error),
230            ul_sys::ULMessageLevel_kMessageLevel_Debug => Ok(ConsoleMessageLevel::Debug),
231            ul_sys::ULMessageLevel_kMessageLevel_Info => Ok(ConsoleMessageLevel::Info),
232            _ => Err(()),
233        }
234    }
235}
236
237/// Configuration to be used when creating a [`View`].
238pub struct ViewConfig {
239    lib: Arc<Library>,
240    internal: ul_sys::ULViewConfig,
241}
242
243impl ViewConfig {
244    /// Starts the building process for the [`ViewConfig`] struct. returns a builder
245    /// which can be used to configure the settings.
246    pub fn start() -> ViewConfigBuilder {
247        ViewConfigBuilder::default()
248    }
249
250    /// Returns the underlying [`ul_sys::ULViewConfig`] struct, to be used locally for
251    /// calling the underlying C API.
252    pub(crate) unsafe fn to_ul(&self) -> ul_sys::ULViewConfig {
253        self.internal
254    }
255}
256
257impl Drop for ViewConfig {
258    fn drop(&mut self) {
259        unsafe {
260            self.lib.ultralight().ulDestroyViewConfig(self.internal);
261        }
262    }
263}
264
265/// Builder for the [`ViewConfig`] struct.
266#[derive(Default)]
267pub struct ViewConfigBuilder {
268    is_accelerated: Option<bool>,
269    is_transparent: Option<bool>,
270    initial_device_scale: Option<f64>,
271    initial_focus: Option<bool>,
272    enable_images: Option<bool>,
273    enable_javascript: Option<bool>,
274    font_family_standard: Option<String>,
275    font_family_fixed: Option<String>,
276    font_family_serif: Option<String>,
277    font_family_sans_serif: Option<String>,
278    user_agent: Option<String>,
279    // display_id: Option<u32>,
280}
281
282impl ViewConfigBuilder {
283    /// Whether to render using the GPU renderer (accelerated) or the CPU renderer (unaccelerated).
284    ///
285    /// When `true`, the View will be rendered to an offscreen GPU texture using the GPU driver set in
286    /// [`platform::set_gpu_driver`](crate::platform::set_gpu_driver).
287    /// You can fetch details for the texture via [`View::render_target`].
288    ///
289    /// When `false` (the default), the View will be rendered to an offscreen
290    /// pixel buffer using the multithreaded CPU renderer.
291    pub fn is_accelerated(mut self, is_accelerated: bool) -> Self {
292        self.is_accelerated = Some(is_accelerated);
293        self
294    }
295
296    /// Whether or not this View should support transparency.
297    ///
298    /// Make sure to also set the following CSS on the page:
299    /// ```css
300    /// html, body { background: transparent; }
301    /// ```
302    ///
303    /// default is `false`
304    pub fn is_transparent(mut self, is_transparent: bool) -> Self {
305        self.is_transparent = Some(is_transparent);
306        self
307    }
308
309    /// The initial device scale, ie. the amount to scale page units to screen
310    /// pixels. This should be set to the scaling factor of the device that
311    /// the View is displayed on.
312    ///
313    /// 1.0 is equal to 100% zoom (no scaling), 2.0 is equal to 200% zoom (2x scaling)
314    ///
315    /// default is `1.0`
316    pub fn initial_device_scale(mut self, initial_device_scale: f64) -> Self {
317        self.initial_device_scale = Some(initial_device_scale);
318        self
319    }
320
321    /// Whether or not the View should initially have input focus, [`View::focus`].
322    ///
323    /// default is `false`
324    pub fn initial_focus(mut self, initial_focus: bool) -> Self {
325        self.initial_focus = Some(initial_focus);
326        self
327    }
328
329    /// Whether or not images should be enabled.
330    ///
331    /// default is `true`
332    pub fn enable_images(mut self, enable_images: bool) -> Self {
333        self.enable_images = Some(enable_images);
334        self
335    }
336
337    /// Whether or not JavaScript should be enabled.
338    ///
339    /// default is `true`
340    pub fn enable_javascript(mut self, enable_javascript: bool) -> Self {
341        self.enable_javascript = Some(enable_javascript);
342        self
343    }
344
345    /// Default font-family to use.
346    ///
347    /// default is `"Times New Roman"`
348    pub fn font_family_standard(mut self, font_family_standard: &str) -> Self {
349        self.font_family_standard = Some(font_family_standard.to_string());
350        self
351    }
352
353    /// Default font-family to use for fixed fonts. (pre/code)
354    ///
355    /// default is `"Courier New"`
356    pub fn font_family_fixed(mut self, font_family_fixed: &str) -> Self {
357        self.font_family_fixed = Some(font_family_fixed.to_string());
358        self
359    }
360
361    /// Default font-family to use for serif fonts.
362    ///
363    /// default is `"Times New Roman"`
364    pub fn font_family_serif(mut self, font_family_serif: &str) -> Self {
365        self.font_family_serif = Some(font_family_serif.to_string());
366        self
367    }
368
369    /// Default font-family to use for sans-serif fonts.
370    ///
371    /// default is `"Arial"`
372    pub fn font_family_sans_serif(mut self, font_family_sans_serif: &str) -> Self {
373        self.font_family_sans_serif = Some(font_family_sans_serif.to_string());
374        self
375    }
376
377    /// User-agent string to use.
378    ///
379    /// default is
380    /// `"Mozilla/5.0 (Windows NT 10.0; Win64; x64)
381    ///   AppleWebKit/608.3.10 (KHTML, like Gecko)
382    ///   Ultralight/1.3.0 Safari/608.3.10"`
383    pub fn user_agent(mut self, user_agent: &str) -> Self {
384        self.user_agent = Some(user_agent.to_string());
385        self
386    }
387
388    // TODO: ulViewConfigSetDisplayId isn't found in the library we use from github, but is found
389    //       in the one from the website. fix that.
390    // /// A user-generated id for the display (monitor, TV, or screen) that this View will be shown on.
391    // ///
392    // /// Animations are driven based on the physical refresh rate of the display. Multiple Views can
393    // /// share the same display.
394    // ///
395    // /// Note: This is automatically managed for you when [`App`][crate::app::App] is used.
396    // ///
397    // /// See also [`Renderer::refresh_display`][crate::renderer::Renderer::refresh_display].
398    // pub fn display_id(mut self, display_id: u32) -> Self {
399    //     self.display_id = Some(display_id);
400    //     self
401    // }
402
403    /// Builds the [`ViewConfig`] struct using the settings configured in this builder.
404    ///
405    /// Returns [`None`] if failed to create [`ViewConfig`].
406    pub fn build(self, lib: Arc<Library>) -> Option<ViewConfig> {
407        let internal = unsafe { lib.ultralight().ulCreateViewConfig() };
408
409        if internal.is_null() {
410            return None;
411        }
412
413        set_config!(
414            internal,
415            self.is_accelerated,
416            lib.ultralight().ulViewConfigSetIsAccelerated
417        );
418        set_config!(
419            internal,
420            self.is_transparent,
421            lib.ultralight().ulViewConfigSetIsTransparent
422        );
423        set_config!(
424            internal,
425            self.initial_device_scale,
426            lib.ultralight().ulViewConfigSetInitialDeviceScale
427        );
428        set_config!(
429            internal,
430            self.initial_focus,
431            lib.ultralight().ulViewConfigSetInitialFocus
432        );
433        set_config!(
434            internal,
435            self.enable_images,
436            lib.ultralight().ulViewConfigSetEnableImages
437        );
438        set_config!(
439            internal,
440            self.enable_javascript,
441            lib.ultralight().ulViewConfigSetEnableJavaScript
442        );
443        set_config_str!(
444            internal,
445            self.font_family_standard,
446            lib.ultralight().ulViewConfigSetFontFamilyStandard
447        );
448        set_config_str!(
449            internal,
450            self.font_family_fixed,
451            lib.ultralight().ulViewConfigSetFontFamilyFixed
452        );
453        set_config_str!(
454            internal,
455            self.font_family_serif,
456            lib.ultralight().ulViewConfigSetFontFamilySerif
457        );
458        set_config_str!(
459            internal,
460            self.font_family_sans_serif,
461            lib.ultralight().ulViewConfigSetFontFamilySansSerif
462        );
463        set_config_str!(
464            internal,
465            self.user_agent,
466            lib.ultralight().ulViewConfigSetUserAgent
467        );
468        // set_config!(internal, self.display_id, ulViewConfigSetDisplayId);
469
470        Some(ViewConfig { lib, internal })
471    }
472}
473
474/// An RAII implementation of a “scoped lock” of a pixel buffer for Javascript Context
475/// of [`View`].
476/// When this structure is dropped (falls out of scope), the lock will be unlocked.
477///
478/// This struct is created by [`View::lock_js_context`].
479///
480/// This can be used as [`Deref`] to access the underlying [`JSContext`].
481pub struct ViewJSContextGuard<'a> {
482    view: &'a View,
483    js_ctx: JSContext,
484}
485
486impl Deref for ViewJSContextGuard<'_> {
487    type Target = JSContext;
488
489    fn deref(&self) -> &Self::Target {
490        &self.js_ctx
491    }
492}
493
494impl Drop for ViewJSContextGuard<'_> {
495    fn drop(&mut self) {
496        unsafe {
497            self.view
498                .lib
499                .ultralight()
500                .ulViewUnlockJSContext(self.view.internal);
501        }
502    }
503}
504
505/// The View class is used to load and display web content.
506///
507/// View is an offscreen web-page container that can be used to display web-content in your
508/// application.
509///
510/// You can load content into a View via [`View::load_url`] or [`View::load_html`]
511/// and interact with it via [`View::fire_mouse_event`] and similar API.
512///
513/// When displaying a View, the API is different depending on whether you are
514/// using the CPU renderer or the GPU renderer:
515///
516/// When using the CPU renderer, you would get the underlying pixel-buffer
517/// surface for a View via [`View::surface`].
518///
519/// When using the GPU renderer, you would get the underlying render target
520/// and texture information via [`View::render_target`].
521pub struct View {
522    lib: Arc<Library>,
523    internal: ul_sys::ULView,
524    need_to_destroy: bool,
525}
526
527impl View {
528    /// Helper internal function to allow getting a reference to a managed
529    /// session.
530    pub(crate) unsafe fn from_raw(lib: Arc<Library>, raw: ul_sys::ULView) -> Option<Self> {
531        if raw.is_null() {
532            None
533        } else {
534            Some(Self {
535                lib,
536                internal: raw,
537                need_to_destroy: false,
538            })
539        }
540    }
541
542    /// Internal function helper to create a view.
543    /// (See [`Renderer::create_view`](crate::renderer::Renderer::create_view))
544    pub(crate) unsafe fn create(
545        renderer: ul_sys::ULRenderer,
546        width: u32,
547        height: u32,
548        view_config: &ViewConfig,
549        session: Option<&Session>,
550    ) -> Option<Self> {
551        let lib = view_config.lib.clone();
552        let internal = lib.ultralight().ulCreateView(
553            renderer,
554            width,
555            height,
556            view_config.to_ul(),
557            session.map(|s| s.to_ul()).unwrap_or(std::ptr::null_mut()),
558        );
559
560        if internal.is_null() {
561            None
562        } else {
563            Some(Self {
564                lib,
565                internal,
566                need_to_destroy: true,
567            })
568        }
569    }
570
571    /// Returns the underlying [`ul_sys::ULView`] struct, to be used locally for
572    /// calling the underlying C API.
573    #[allow(dead_code)]
574    pub(crate) unsafe fn to_ul(&self) -> ul_sys::ULView {
575        self.internal
576    }
577}
578
579impl View {
580    /// Get the URL of the current page loaded into this View, if any.
581    pub fn url(&self) -> Result<String, CreationError> {
582        unsafe {
583            let url_string = self.lib.ultralight().ulViewGetURL(self.internal);
584            UlString::copy_raw_to_string(&self.lib, url_string)
585        }
586    }
587
588    /// Get the title of the current page loaded into this View, if any.
589    pub fn title(&self) -> Result<String, CreationError> {
590        unsafe {
591            let title_string = self.lib.ultralight().ulViewGetTitle(self.internal);
592            UlString::copy_raw_to_string(&self.lib, title_string)
593        }
594    }
595
596    /// Get the width of the View, in pixels.
597    pub fn width(&self) -> u32 {
598        unsafe { self.lib.ultralight().ulViewGetWidth(self.internal) }
599    }
600
601    /// Get the height of the View, in pixels.
602    pub fn height(&self) -> u32 {
603        unsafe { self.lib.ultralight().ulViewGetHeight(self.internal) }
604    }
605
606    /// Get the device scale, ie. the amount to scale page units to screen pixels.
607    ///
608    /// For example, a value of 1.0 is equivalent to 100% zoom. A value of 2.0 is 200% zoom.
609    pub fn device_scale(&self) -> f64 {
610        unsafe { self.lib.ultralight().ulViewGetDeviceScale(self.internal) }
611    }
612
613    /// Set the device scale.
614    pub fn set_device_scale(&self, scale: f64) {
615        unsafe {
616            self.lib
617                .ultralight()
618                .ulViewSetDeviceScale(self.internal, scale)
619        }
620    }
621
622    /// Whether or not the View is GPU-accelerated. If this is false,
623    /// the page will be rendered via the CPU renderer.
624    pub fn is_accelerated(&self) -> bool {
625        unsafe { self.lib.ultralight().ulViewIsAccelerated(self.internal) }
626    }
627
628    /// Whether or not the View supports transparent backgrounds.
629    pub fn is_transparent(&self) -> bool {
630        unsafe { self.lib.ultralight().ulViewIsTransparent(self.internal) }
631    }
632
633    /// Check if the main frame of the page is currently loading.
634    pub fn is_loading(&self) -> bool {
635        unsafe { self.lib.ultralight().ulViewIsLoading(self.internal) }
636    }
637
638    /// Get the RenderTarget for the View.
639    ///
640    /// Only valid when the view is accelerated, and will return [`None`] otherwise.
641    pub fn render_target(&self) -> Option<RenderTarget> {
642        if self.is_accelerated() {
643            Some(unsafe {
644                RenderTarget::from(self.lib.ultralight().ulViewGetRenderTarget(self.internal))
645            })
646        } else {
647            None
648        }
649    }
650
651    /// Get the Surface for the View (native pixel buffer container).
652    ///
653    /// Only valid when the view is not accelerated, and will return [`None`] otherwise.
654    pub fn surface(&self) -> Option<Surface> {
655        if !self.is_accelerated() {
656            unsafe {
657                let surface = self.lib.ultralight().ulViewGetSurface(self.internal);
658                if surface.is_null() {
659                    None
660                } else {
661                    Some(Surface::from_raw(self.lib.clone(), surface))
662                }
663            }
664        } else {
665            None
666        }
667    }
668
669    /// Load a raw string of HTML, the View will navigate to it as a new page.
670    pub fn load_html(&self, html: &str) -> Result<(), CreationError> {
671        unsafe {
672            let ul_string = UlString::from_str(self.lib.clone(), html)?;
673            self.lib
674                .ultralight()
675                .ulViewLoadHTML(self.internal, ul_string.to_ul());
676        }
677        Ok(())
678    }
679
680    /// Load a URL, the View will navigate to it as a new page.
681    ///
682    /// You can use File URLs (eg, file:///page.html) as well.
683    pub fn load_url(&self, url: &str) -> Result<(), CreationError> {
684        unsafe {
685            let ul_string = UlString::from_str(self.lib.clone(), url)?;
686            self.lib
687                .ultralight()
688                .ulViewLoadURL(self.internal, ul_string.to_ul());
689        }
690        Ok(())
691    }
692
693    /// Resize View to a certain size.
694    ///
695    /// # Arguments
696    /// * `width` - The new width in pixels.
697    /// * `height` - The new height in pixels.
698    pub fn resize(&self, width: u32, height: u32) {
699        unsafe {
700            self.lib
701                .ultralight()
702                .ulViewResize(self.internal, width, height);
703        }
704    }
705
706    /// Acquire the page's [`JSContext`] for use with JavaScriptCore API.
707    ///
708    /// Note: This call locks the context for the current thread. You should call
709    /// You should drop this so that the context is unlocked so that other threads
710    /// can use this context.
711    ///
712    /// The lock is recusive, it's okay to aquaire this multiple times, but you should drop
713    /// all the instances to unlock the context.
714    pub fn lock_js_context(&self) -> ViewJSContextGuard {
715        let ctx = unsafe { self.lib.ultralight().ulViewLockJSContext(self.internal) };
716
717        let js_ctx = JSContext::copy_from_raw(self.lib.clone(), ctx);
718
719        ViewJSContextGuard { view: self, js_ctx }
720    }
721
722    /// Helper function to evaluate a raw string of JavaScript and return the result as a String.
723    ///
724    /// You can pass the raw Javascript string in `script`, if an exception occurs
725    /// it will be returned in [`Err`], otherwise a string result in [`Ok`] will be
726    /// returned.
727    pub fn evaluate_script(&self, script: &str) -> Result<Result<String, String>, CreationError> {
728        unsafe {
729            let ul_script_string = UlString::from_str(self.lib.clone(), script)?;
730            // a dummy value, it will be replaced by the actual result
731            let mut exception_string = 1 as ul_sys::ULString;
732            let result_string = self.lib.ultralight().ulViewEvaluateScript(
733                self.internal,
734                ul_script_string.to_ul(),
735                &mut exception_string as _,
736            );
737
738            let has_exception = !self.lib.ultralight().ulStringIsEmpty(exception_string);
739            if has_exception {
740                let exception_string = UlString::copy_raw_to_string(&self.lib, exception_string)?;
741                Ok(Err(exception_string))
742            } else {
743                let result_string = UlString::copy_raw_to_string(&self.lib, result_string)?;
744                Ok(Ok(result_string))
745            }
746        }
747    }
748
749    /// Whether or not we can navigate backwards in history
750    pub fn can_go_back(&self) -> bool {
751        unsafe { self.lib.ultralight().ulViewCanGoBack(self.internal) }
752    }
753
754    /// Whether or not we can navigate forwards in history
755    pub fn can_go_forward(&self) -> bool {
756        unsafe { self.lib.ultralight().ulViewCanGoForward(self.internal) }
757    }
758
759    /// Navigate backwards in history
760    pub fn go_back(&self) {
761        unsafe { self.lib.ultralight().ulViewGoBack(self.internal) }
762    }
763
764    /// Navigate forwards in history
765    pub fn go_forward(&self) {
766        unsafe { self.lib.ultralight().ulViewGoForward(self.internal) }
767    }
768
769    /// Navigate to an arbitrary offset in history
770    pub fn go_to_history_offset(&self, offset: i32) {
771        unsafe {
772            self.lib
773                .ultralight()
774                .ulViewGoToHistoryOffset(self.internal, offset)
775        }
776    }
777
778    /// Reload current page
779    pub fn reload(&self) {
780        unsafe { self.lib.ultralight().ulViewReload(self.internal) }
781    }
782
783    /// Stop all page loads
784    pub fn stop(&self) {
785        unsafe { self.lib.ultralight().ulViewStop(self.internal) }
786    }
787
788    /// Give focus to the View.
789    ///
790    /// You should call this to give visual indication that the View
791    /// has input focus (changes active text selection colors, for example).
792    pub fn focus(&self) {
793        unsafe { self.lib.ultralight().ulViewFocus(self.internal) }
794    }
795
796    /// Remove focus from the View and unfocus any focused input elements.
797    ///
798    /// You should call this to give visual indication that the View has lost input focus.
799    pub fn unfocus(&self) {
800        unsafe { self.lib.ultralight().ulViewUnfocus(self.internal) }
801    }
802
803    /// Whether or not the View has focus.
804    pub fn has_focus(&self) -> bool {
805        unsafe { self.lib.ultralight().ulViewHasFocus(self.internal) }
806    }
807
808    /// Whether or not the View has an input element with visible keyboard focus
809    /// (indicated by a blinking caret).
810    ///
811    /// You can use this to decide whether or not the View should consume
812    /// keyboard input events (useful in games with mixed UI and key handling).
813    pub fn has_input_focus(&self) -> bool {
814        unsafe { self.lib.ultralight().ulViewHasInputFocus(self.internal) }
815    }
816
817    /// Fire a keyboard event
818    ///
819    /// Note that only [`KeyEventType::Char`](crate::event::KeyEventType::Char)
820    /// events actually generate text in input fields.
821    pub fn fire_key_event(&self, key_event: KeyEvent) {
822        unsafe {
823            self.lib
824                .ultralight()
825                .ulViewFireKeyEvent(self.internal, key_event.to_ul())
826        }
827    }
828
829    /// Fire a mouse event
830    pub fn fire_mouse_event(&self, mouse_event: MouseEvent) {
831        unsafe {
832            self.lib
833                .ultralight()
834                .ulViewFireMouseEvent(self.internal, mouse_event.to_ul())
835        }
836    }
837
838    /// Fire a scroll event
839    pub fn fire_scroll_event(&self, scroll_event: ScrollEvent) {
840        unsafe {
841            self.lib
842                .ultralight()
843                .ulViewFireScrollEvent(self.internal, scroll_event.to_ul())
844        }
845    }
846
847    /// Get the display id of the View.
848    pub fn get_display_id(&self) -> u32 {
849        unsafe { self.lib.ultralight().ulViewGetDisplayId(self.internal) }
850    }
851
852    /// Set the display id of the View.
853    ///
854    /// This should be called when the View is moved to another display.
855    pub fn set_display_id(&self, display_id: u32) {
856        unsafe {
857            self.lib
858                .ultralight()
859                .ulViewSetDisplayId(self.internal, display_id)
860        }
861    }
862
863    // looking at the CPP header, the strings seems to be references
864    // but the C headers doesn't say we must not destroy them.
865    // For now we don't destroy.
866    //  TODO: check if we don't need to destroy them
867    set_callback! {
868        /// Called when the page title changes
869        ///
870        /// # Callback Arguments
871        /// * `view: &View` - The view that fired the event (eg. self)
872        /// * `title: String` - The new title
873        pub fn set_change_title_callback(&self, callback: FnMut(view: &View, title: String)) :
874            [View::lib.ultralight()][s] ulViewSetChangeTitleCallback(ul_view: ul_sys::ULView, ul_title: ul_sys::ULString) {
875               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
876               let title = UlString::copy_raw_to_string(&s.lib,ul_title).unwrap();
877        }
878    }
879
880    set_callback! {
881        /// Called when the page URL changes
882        ///
883        /// # Callback Arguments
884        /// * `view: &View` - The view that fired the event (eg. self)
885        /// * `url: String` - The new url
886        pub fn set_change_url_callback(&self, callback: FnMut(view: &View, url: String)) :
887            [View::lib.ultralight()][s] ulViewSetChangeURLCallback(ul_view: ul_sys::ULView, ul_url: ul_sys::ULString) {
888               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
889               let url = UlString::copy_raw_to_string(&s.lib,ul_url).unwrap();
890        }
891    }
892
893    set_callback! {
894        /// Called when the tooltip changes (usually as result of a mouse hover)
895        ///
896        /// # Callback Arguments
897        /// * `view: &View` - The view that fired the event (eg. self)
898        /// * `tooltip: String` - The tooltip string
899        pub fn set_change_tooltip_callback(&self, callback: FnMut(view: &View, tooltip: String)) :
900            [View::lib.ultralight()][s] ulViewSetChangeTooltipCallback(ul_view: ul_sys::ULView, ul_tooltip: ul_sys::ULString) {
901               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
902               let tooltip = UlString::copy_raw_to_string(&s.lib,ul_tooltip).unwrap();
903        }
904    }
905
906    set_callback! {
907        /// Called when the mouse cursor changes
908        ///
909        /// # Callback Arguments
910        /// * `view: &View` - The view that fired the event (eg. self)
911        /// * `cursor: Cursor` - The cursor type
912        pub fn set_change_cursor_callback(&self, callback: FnMut(view: &View, cursor: Cursor)) :
913            [View::lib.ultralight()][s] ulViewSetChangeCursorCallback(ul_view: ul_sys::ULView, ul_cursor: ul_sys::ULCursor) {
914               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
915               let cursor = Cursor::try_from(ul_cursor).unwrap();
916        }
917    }
918
919    set_callback! {
920        /// Called when a message is added to the console (useful for errors / debug)
921        ///
922        /// # Callback Arguments
923        /// * `view: &View` - The view that fired the event (eg. self)
924        /// * `message_source: ConsoleMessageSource` - The source of the message
925        /// * `message_level: ConsoleMessageLevel` - The level of the message
926        /// * `message: String` - The message
927        /// * `line_number: i32` - The line number of the message
928        /// * `column_number: i32` - The column number of the message
929        /// * `source_id: String` - The source id of the message
930        pub fn set_add_console_message_callback(&self, callback: FnMut(
931                view: &View,
932                message_source: ConsoleMessageSource,
933                message_level: ConsoleMessageLevel,
934                message: String,
935                line_number:u32,
936                column_number:u32,
937                source_id: String)) :
938            [View::lib.ultralight()][s] ulViewSetAddConsoleMessageCallback(
939               ul_view: ul_sys::ULView,
940               ul_message_source: ul_sys::ULMessageSource,
941               ul_message_level: ul_sys::ULMessageLevel,
942               ul_message: ul_sys::ULString,
943               line_number: u32,
944               column_number :u32,
945               ul_source_id: ul_sys::ULString
946            ) {
947               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
948               let message_source = ConsoleMessageSource::try_from(ul_message_source).unwrap();
949               let message_level = ConsoleMessageLevel::try_from(ul_message_level).unwrap();
950               let message = UlString::copy_raw_to_string(&s.lib,ul_message).unwrap();
951               let source_id = UlString::copy_raw_to_string(&s.lib,ul_source_id).unwrap();
952        }
953    }
954
955    set_callback! {
956        // TODO: this callback require that you return owned `View`
957        //       but because you have to render it yourself, this won't do,
958        //       its better to return a reference, but not sure how we should
959        //       manage the owner and lifetime.
960        //       You can return `None` and create a new view since you have
961        //       the `url` and all information needed to create it.
962        //
963        /// Set callback for when the page wants to create a new View.
964        ///
965        /// This is usually the result of a user clicking a link with
966        /// `target="_blank"` or by JavaScript calling `window.open(url)`.
967        ///
968        /// To allow creation of these new Views, you should create a new View
969        /// in this callback (eg. [`Renderer::create_view`](crate::renderer::Renderer::create_view)),
970        /// resize it to your container, and return it.
971        /// You are responsible for displaying the returned View.
972        ///
973        /// # Callback Arguments
974        /// * `view: &View` - The view that fired the event (eg. self)
975        /// * `opener_url: String` - The url of the page that initiated this request
976        /// * `target_url: String` - The url the new View will navigate to
977        /// * `is_popup: bool` - Whether or not this was triggered by window.open()
978        /// * `popup_rect: Rect<i32>` - Popups can optionally request certain
979        /// dimensions and coordinates via window.open(). You can choose to
980        /// respect these or not by resizing/moving the View to this rect.
981        ///
982        /// You should return [`None`] if you want to block the action.
983        pub fn set_create_child_view_callback(&self, callback: FnMut(
984                view: &View,
985                opener_url: String,
986                target_url: String,
987                is_popup: bool,
988                popup_rect: Rect<i32>
989                // TODO: should we change the return type?
990                //       since the new view will be owned by another overlay
991            ) -> ret_view: Option<View>) :
992            [View::lib.ultralight()][s] ulViewSetCreateChildViewCallback(
993               ul_view: ul_sys::ULView,
994               ul_opener_url: ul_sys::ULString,
995               ul_target_url: ul_sys::ULString,
996               is_popup: bool,
997               ul_popup_rect: ul_sys::ULIntRect
998            ) -> ul_sys::ULView {
999               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1000               let opener_url = UlString::copy_raw_to_string(&s.lib,ul_opener_url).unwrap();
1001               let target_url = UlString::copy_raw_to_string(&s.lib,ul_target_url).unwrap();
1002               let popup_rect = Rect::from(ul_popup_rect);
1003        } {
1004            if let Some(ret_view) = ret_view {
1005                ret_view.internal
1006            } else {
1007                std::ptr::null_mut()
1008            }
1009        }
1010    }
1011
1012    set_callback! {
1013        // TODO: this callback require that you return owned `View`
1014        //       but because you have to render it yourself, this won't do,
1015        //       its better to return a reference, but not sure how we should
1016        //       manage the owner and lifetime.
1017        //       You can return `None` and create a new view since you have
1018        //       the `url` and all information needed to create it.
1019        //
1020        /// Set callback for when the page wants to create a new View to display the
1021        /// local inspector in.
1022        ///
1023        /// See also [`View::create_local_inspector_view`].
1024        ///
1025        /// To allow creation of these new Views, you should create a new View
1026        /// in this callback (eg. [`Renderer::create_view`](crate::renderer::Renderer::create_view)),
1027        /// resize it to your container, and return it.
1028        /// You are responsible for displaying the returned View.
1029        ///
1030        /// # Callback Arguments
1031        /// * `view: &View` - The view that fired the event (eg. self)
1032        /// * `is_local: bool` - Whether or not this inspector view is local
1033        /// * `inspected_url: String` - The url of the page that initiated this request
1034        ///
1035        /// You should return [`None`] if you want to block the action.
1036        pub fn set_create_inspector_view_callback(&self, callback: FnMut(
1037                view: &View,
1038                is_local: bool,
1039                inspected_url: String
1040                // TODO: should we change the return type?
1041                //       since the new view will be owned by another overlay
1042            ) -> ret_view: Option<View>) :
1043            [View::lib.ultralight()][s] ulViewSetCreateInspectorViewCallback(
1044               ul_view: ul_sys::ULView,
1045               is_local: bool,
1046               ul_inspected_url: ul_sys::ULString
1047            ) -> ul_sys::ULView {
1048               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1049                let inspected_url = UlString::copy_raw_to_string(&s.lib,ul_inspected_url).unwrap();
1050        } {
1051            if let Some(ret_view) = ret_view {
1052                ret_view.internal
1053            } else {
1054                std::ptr::null_mut()
1055            }
1056        }
1057    }
1058
1059    set_callback! {
1060        /// Called when the page begins loading a new URL into a frame.
1061        ///
1062        /// # Callback Arguments
1063        /// * `view: &View` - The view that fired the event (eg. self)
1064        /// * `frame_id: u64` - A unique ID for the frame
1065        /// * `is_main_frame: bool` - Whether or not this is the main frame
1066        /// * `url: String` - The url that is being loaded
1067        pub fn set_begin_loading_callback(&self, callback: FnMut(
1068                view: &View,
1069                frame_id: u64,
1070                is_main_frame: bool,
1071                url: String)) :
1072            [View::lib.ultralight()][s] ulViewSetBeginLoadingCallback(
1073               ul_view: ul_sys::ULView,
1074               frame_id: u64,
1075               is_main_frame: bool,
1076               ul_url: ul_sys::ULString
1077            ) {
1078               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1079               let url = UlString::copy_raw_to_string(&s.lib,ul_url).unwrap();
1080        }
1081    }
1082
1083    set_callback! {
1084        /// Called when the page finishes loading a URL into a frame.
1085        ///
1086        /// # Callback Arguments
1087        /// * `view: &View` - The view that fired the event (eg. self)
1088        /// * `frame_id: u64` - A unique ID for the frame
1089        /// * `is_main_frame: bool` - Whether or not this is the main frame
1090        /// * `url: String` - The url that is being loaded
1091        pub fn set_finish_loading_callback(&self, callback: FnMut(
1092                view: &View,
1093                frame_id: u64,
1094                is_main_frame: bool,
1095                url: String)) :
1096            [View::lib.ultralight()][s] ulViewSetFinishLoadingCallback(
1097               ul_view: ul_sys::ULView,
1098               frame_id: u64,
1099               is_main_frame: bool,
1100               ul_url: ul_sys::ULString
1101            ) {
1102               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1103               let url = UlString::copy_raw_to_string(&s.lib,ul_url).unwrap();
1104        }
1105    }
1106
1107    set_callback! {
1108        /// Called when an error occurs while loading a URL into a frame.
1109        ///
1110        /// # Callback Arguments
1111        /// * `view: &View` - The view that fired the event (eg. self)
1112        /// * `frame_id: u64` - A unique ID for the frame
1113        /// * `is_main_frame: bool` - Whether or not this is the main frame
1114        /// * `url: String` - The url that is being loaded
1115        /// * `description: String` -  A human-readable description of the error.
1116        /// * `error_domain: String` - The name of the module that triggered the error.
1117        /// * `error_code: u32` - Internal error code generated by the module
1118        pub fn set_fail_loading_callback(&self, callback: FnMut(
1119                view: &View,
1120                frame_id: u64,
1121                is_main_frame: bool,
1122                url: String,
1123                description: String,
1124                error_domain: String,
1125                error_code: i32)) :
1126            [View::lib.ultralight()][s] ulViewSetFailLoadingCallback(
1127               ul_view: ul_sys::ULView,
1128               frame_id: u64,
1129               is_main_frame: bool,
1130               ul_url: ul_sys::ULString,
1131               ul_description: ul_sys::ULString,
1132               ul_error_domain: ul_sys::ULString,
1133               error_code: i32
1134            ) {
1135               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1136               let url = UlString::copy_raw_to_string(&s.lib,ul_url).unwrap();
1137               let description = UlString::copy_raw_to_string(&s.lib,ul_description).unwrap();
1138               let error_domain = UlString::copy_raw_to_string(&s.lib,ul_error_domain).unwrap();
1139        }
1140    }
1141
1142    set_callback! {
1143        /// Set callback for when the JavaScript window object is reset for a new page load.
1144        ///
1145        /// This is called before any scripts are executed on the page and is the earliest time to setup any
1146        /// initial JavaScript state or bindings.
1147        ///
1148        /// The document is not guaranteed to be loaded/parsed at this point. If you need to make any
1149        /// JavaScript calls that are dependent on DOM elements or scripts on the page, use DOMReady
1150        /// instead.
1151        ///
1152        /// The window object is lazily initialized (this will not be called on pages with no scripts).
1153        ///
1154        /// # Callback Arguments
1155        /// * `view: &View` - The view that fired the event (eg. self)
1156        /// * `frame_id: u64` - A unique ID for the frame
1157        /// * `is_main_frame: bool` - Whether or not this is the main frame
1158        /// * `url: String` - The url that is being loaded
1159        pub fn set_window_object_ready_callback(&self, callback: FnMut(
1160                view: &View,
1161                frame_id: u64,
1162                is_main_frame: bool,
1163                url: String)) :
1164            [View::lib.ultralight()][s] ulViewSetWindowObjectReadyCallback(
1165               ul_view: ul_sys::ULView,
1166               frame_id: u64,
1167               is_main_frame: bool,
1168               ul_url: ul_sys::ULString
1169            ) {
1170               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1171               let url = UlString::copy_raw_to_string(&s.lib,ul_url).unwrap();
1172        }
1173    }
1174
1175    set_callback! {
1176        /// Called when all JavaScript has been parsed and the document is ready.
1177        ///
1178        /// This is the best time to make any JavaScript calls that are dependent
1179        /// on DOM elements or scripts on the page.
1180        ///
1181        /// # Callback Arguments
1182        /// * `view: &View` - The view that fired the event (eg. self)
1183        /// * `frame_id: u64` - A unique ID for the frame
1184        /// * `is_main_frame: bool` - Whether or not this is the main frame
1185        /// * `url: String` - The url that is being loaded
1186        pub fn set_dom_ready_callback(&self, callback: FnMut(
1187                view: &View,
1188                frame_id: u64,
1189                is_main_frame: bool,
1190                url: String)) :
1191            [View::lib.ultralight()][s] ulViewSetDOMReadyCallback(
1192               ul_view: ul_sys::ULView,
1193               frame_id: u64,
1194               is_main_frame: bool,
1195               ul_url: ul_sys::ULString
1196            ) {
1197               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1198               let url = UlString::copy_raw_to_string(&s.lib, ul_url).unwrap();
1199        }
1200    }
1201
1202    set_callback! {
1203        /// Called when the session history (back/forward state) is modified.
1204        ///
1205        /// # Callback Arguments
1206        /// * `view: &View` - The view that fired the event (eg. self)
1207        pub fn set_update_history_callback(&self, callback: FnMut(view: &View)) :
1208           [View::lib.ultralight()][s] ulViewSetUpdateHistoryCallback(ul_view: ul_sys::ULView) {
1209               let view = &View::from_raw(s.lib.clone(), ul_view).unwrap();
1210        }
1211    }
1212
1213    /// Set whether or not this View should be repainted during the next call
1214    /// to [`Renderer::render`](crate::renderer::Renderer::render).
1215    ///
1216    /// This flag is automatically set whenever the page content changes but
1217    /// you can set it directly in case you need to force a repaint.
1218    pub fn set_needs_paint(&self, needs_paint: bool) {
1219        unsafe {
1220            self.lib
1221                .ultralight()
1222                .ulViewSetNeedsPaint(self.internal, needs_paint)
1223        }
1224    }
1225
1226    /// Whether or not this View should be repainted during the next call to
1227    /// [`Renderer::render`](crate::renderer::Renderer::render).
1228    pub fn needs_paint(&self) -> bool {
1229        unsafe { self.lib.ultralight().ulViewGetNeedsPaint(self.internal) }
1230    }
1231
1232    /// Create an Inspector View to inspect / debug this View locally
1233    ///
1234    /// This will only succeed if you have the
1235    /// inspector assets in your filesystem-- the inspector will
1236    /// look for `file:///inspector/Main.html` when it loads.
1237    ///
1238    /// You must handle [`View::set_create_inspector_view_callback`] so that
1239    /// the library has a View to display the inspector in. This function will
1240    /// call the callback only if an inspector view is not currently active.
1241    pub fn create_local_inspector_view(&self) {
1242        unsafe {
1243            self.lib
1244                .ultralight()
1245                .ulViewCreateLocalInspectorView(self.internal);
1246        }
1247    }
1248}
1249
1250impl Drop for View {
1251    fn drop(&mut self) {
1252        if self.need_to_destroy {
1253            unsafe {
1254                self.lib.ultralight().ulDestroyView(self.internal);
1255            }
1256        }
1257    }
1258}