From 78908c5e3cf9374f8caaa8a19d31f2efc31e3fa9 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 6 May 2019 15:25:58 -0700 Subject: [PATCH] Factor the demo UI into model and presenter --- demo/common/src/lib.rs | 51 ++++++---- demo/common/src/renderer.rs | 12 +-- demo/common/src/ui.rs | 189 +++++++++++++++++------------------- 3 files changed, 124 insertions(+), 128 deletions(-) diff --git a/demo/common/src/lib.rs b/demo/common/src/lib.rs index 0fe60715..87b083f0 100644 --- a/demo/common/src/lib.rs +++ b/demo/common/src/lib.rs @@ -16,7 +16,7 @@ extern crate log; use crate::camera::{Camera, Mode}; use crate::concurrent::DemoExecutor; use crate::device::{GroundProgram, GroundVertexArray}; -use crate::ui::{DemoUI, UIAction}; +use crate::ui::{DemoUIModel, DemoUIPresenter, UIAction}; use crate::window::{Event, Keycode, SVGPath, Window, WindowSize}; use clap::{App, Arg}; use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32}; @@ -103,7 +103,9 @@ pub struct DemoApp where W: Window { current_frame: Option, build_time: Option, - ui: DemoUI, + ui_model: DemoUIModel, + ui_presenter: DemoUIPresenter, + scene_proxy: SceneProxy, renderer: Renderer, @@ -147,15 +149,18 @@ impl DemoApp where W: Window { &ground_program, &renderer.quad_vertex_positions_buffer()); - let mut ui = DemoUI::new(&renderer.device, resources, options.clone()); + let mut ui_model = DemoUIModel::new(&options); + let mut message_epoch = 0; emit_message::( - &mut ui, + &mut ui_model, &mut message_epoch, expire_message_event_id, message, ); + let ui_presenter = DemoUIPresenter::new(&renderer.device, resources); + DemoApp { window, should_exit: false, @@ -179,7 +184,9 @@ impl DemoApp where W: Window { current_frame: None, build_time: None, - ui, + ui_presenter, + ui_model, + scene_proxy, renderer, @@ -232,14 +239,14 @@ impl DemoApp where W: Window { let render_options = RenderOptions { transform: self.render_transform.clone().unwrap(), - dilation: if self.ui.stem_darkening_effect_enabled { + dilation: if self.ui_model.stem_darkening_effect_enabled { let font_size = APPROX_FONT_SIZE * self.window_size.backing_scale_factor; let (x, y) = (STEM_DARKENING_FACTORS[0], STEM_DARKENING_FACTORS[1]); Point2DF32::new(x, y).scale(font_size) } else { Point2DF32::default() }, - subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled, + subpixel_aa_enabled: self.ui_model.subpixel_aa_effect_enabled, }; self.render_command_stream = Some(self.scene_proxy.build_with_stream(render_options)); @@ -257,7 +264,7 @@ impl DemoApp where W: Window { } Event::WindowResized(new_size) => { self.window_size = new_size; - let viewport = self.window.viewport(self.ui.mode.view(0)); + let viewport = self.window.viewport(self.ui_model.mode.view(0)); self.scene_proxy.set_view_box(RectF32::new(Point2DF32::default(), viewport.size().to_f32())); self.renderer @@ -392,12 +399,12 @@ impl DemoApp where W: Window { Event::OpenSVG(ref svg_path) => { let mut built_svg = load_scene(self.window.resource_loader(), svg_path); - self.ui.message = get_svg_building_message(&built_svg); + self.ui_model.message = get_svg_building_message(&built_svg); - let viewport_size = self.window.viewport(self.ui.mode.view(0)).size(); + let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); self.scene_metadata = SceneMetadata::new_clipping_view_box(&mut built_svg.scene, viewport_size); - self.camera = Camera::new(self.ui.mode, + self.camera = Camera::new(self.ui_model.mode, self.scene_metadata.view_box, viewport_size); @@ -412,7 +419,7 @@ impl DemoApp where W: Window { } if event_id == self.expire_message_event_id && expected_epoch as u32 == self.message_epoch => { - self.ui.message = String::new(); + self.ui_model.message = String::new(); self.dirty = true; } _ => continue, @@ -444,15 +451,17 @@ impl DemoApp where W: Window { .last_mouse_position .to_f32() .scale(self.window_size.backing_scale_factor); - self.ui.show_text_effects = self.scene_metadata.monochrome_color.is_some(); + + self.ui_presenter.set_show_text_effects(self.scene_metadata.monochrome_color.is_some()); let mut ui_action = UIAction::None; if self.options.ui == UIVisibility::All { - self.ui.update( + self.ui_presenter.update( &self.renderer.device, &mut self.window, &mut self.renderer.debug_ui, &mut ui_action, + &mut self.ui_model, ); } @@ -499,9 +508,11 @@ impl DemoApp where W: Window { // Switch camera mode (2D/3D) if requested. // // FIXME(pcwalton): This should really be an MVC setup. - if self.camera.mode() != self.ui.mode { - let viewport_size = self.window.viewport(self.ui.mode.view(0)).size(); - self.camera = Camera::new(self.ui.mode, self.scene_metadata.view_box, viewport_size); + if self.camera.mode() != self.ui_model.mode { + let viewport_size = self.window.viewport(self.ui_model.mode.view(0)).size(); + self.camera = Camera::new(self.ui_model.mode, + self.scene_metadata.view_box, + viewport_size); } for ui_event in frame.ui_events { @@ -564,7 +575,7 @@ impl DemoApp where W: Window { } fn background_color(&self) -> ColorU { - match self.ui.background_color { + match self.ui_model.background_color { BackgroundColor::Light => LIGHT_BG_COLOR, BackgroundColor::Dark => DARK_BG_COLOR, BackgroundColor::Transparent => TRANSPARENT_BG_COLOR, @@ -711,7 +722,7 @@ fn get_svg_building_message(built_svg: &BuiltSVG) -> String { } fn emit_message( - ui: &mut DemoUI, + ui_model: &mut DemoUIModel, message_epoch: &mut u32, expire_message_event_id: u32, message: String, @@ -722,7 +733,7 @@ fn emit_message( return; } - ui.message = message; + ui_model.message = message; let expected_epoch = *message_epoch + 1; *message_epoch = expected_epoch; thread::spawn(move || { diff --git a/demo/common/src/renderer.rs b/demo/common/src/renderer.rs index e618e693..98632d4f 100644 --- a/demo/common/src/renderer.rs +++ b/demo/common/src/renderer.rs @@ -42,7 +42,7 @@ const GRIDLINE_COUNT: i32 = 10; impl DemoApp where W: Window { pub fn prepare_frame_rendering(&mut self) -> u32 { // Make the GL context current. - let view = self.ui.mode.view(0); + let view = self.ui_model.mode.view(0); self.window.make_current(view); // Set up framebuffers. @@ -101,7 +101,7 @@ impl DemoApp where W: Window { } pub fn draw_scene(&mut self) { - let view = self.ui.mode.view(0); + let view = self.ui_model.mode.view(0); self.window.make_current(view); if self.camera.mode() != Mode::VR { @@ -212,7 +212,7 @@ impl DemoApp where W: Window { RenderTransform::Perspective(perspective) => perspective, }; - if self.ui.background_color == BackgroundColor::Transparent { + if self.ui_model.background_color == BackgroundColor::Transparent { return; } @@ -264,8 +264,8 @@ impl DemoApp where W: Window { self.renderer.set_render_mode(RenderMode::Monochrome { fg_color: fg_color.to_f32(), bg_color: self.background_color().to_f32(), - gamma_correction: self.ui.gamma_correction_effect_enabled, - defringing_kernel: if self.ui.subpixel_aa_effect_enabled { + gamma_correction: self.ui_model.gamma_correction_effect_enabled, + defringing_kernel: if self.ui_model.subpixel_aa_effect_enabled { // TODO(pcwalton): Select FreeType defringing kernel as necessary. Some(DEFRINGING_KERNEL_CORE_GRAPHICS) } else { @@ -275,7 +275,7 @@ impl DemoApp where W: Window { } } - if self.ui.mode == Mode::TwoD { + if self.ui_model.mode == Mode::TwoD { self.renderer.disable_depth(); } else { self.renderer.enable_depth(); diff --git a/demo/common/src/ui.rs b/demo/common/src/ui.rs index 4409828d..a4116598 100644 --- a/demo/common/src/ui.rs +++ b/demo/common/src/ui.rs @@ -44,7 +44,35 @@ static ZOOM_OUT_PNG_NAME: &'static str = "demo-zoom-out"; static BACKGROUND_PNG_NAME: &'static str = "demo-background"; static SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot"; -pub struct DemoUI +pub struct DemoUIModel { + pub mode: Mode, + pub background_color: BackgroundColor, + pub gamma_correction_effect_enabled: bool, + pub stem_darkening_effect_enabled: bool, + pub subpixel_aa_effect_enabled: bool, + pub rotation: i32, + pub message: String, +} + +impl DemoUIModel { + pub fn new(options: &Options) -> DemoUIModel { + DemoUIModel { + mode: options.mode, + background_color: options.background_color, + gamma_correction_effect_enabled: false, + stem_darkening_effect_enabled: false, + subpixel_aa_effect_enabled: false, + rotation: SLIDER_WIDTH / 2, + message: String::new(), + } + } + + fn rotation(&self) -> f32 { + (self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI + } +} + +pub struct DemoUIPresenter where D: Device, { @@ -60,22 +88,14 @@ where background_panel_visible: bool, rotate_panel_visible: bool, - // FIXME(pcwalton): Factor the below out into a model class. - pub mode: Mode, - pub background_color: BackgroundColor, - pub gamma_correction_effect_enabled: bool, - pub stem_darkening_effect_enabled: bool, - pub subpixel_aa_effect_enabled: bool, - pub rotation: i32, - pub message: String, - pub show_text_effects: bool, + show_text_effects: bool, } -impl DemoUI +impl DemoUIPresenter where D: Device, { - pub fn new(device: &D, resources: &dyn ResourceLoader, options: Options) -> DemoUI { + pub fn new(device: &D, resources: &dyn ResourceLoader) -> DemoUIPresenter { let effects_texture = device.create_texture_from_png(resources, EFFECTS_PNG_NAME); let open_texture = device.create_texture_from_png(resources, OPEN_PNG_NAME); let rotate_texture = device.create_texture_from_png(resources, ROTATE_PNG_NAME); @@ -84,7 +104,7 @@ where let background_texture = device.create_texture_from_png(resources, BACKGROUND_PNG_NAME); let screenshot_texture = device.create_texture_from_png(resources, SCREENSHOT_PNG_NAME); - DemoUI { + DemoUIPresenter { effects_texture, open_texture, rotate_texture, @@ -97,19 +117,12 @@ where background_panel_visible: false, rotate_panel_visible: false, - mode: options.mode, - background_color: options.background_color, - gamma_correction_effect_enabled: false, - stem_darkening_effect_enabled: false, - subpixel_aa_effect_enabled: false, - rotation: SLIDER_WIDTH / 2, - message: String::new(), show_text_effects: true, } } - fn rotation(&self) -> f32 { - (self.rotation as f32 / SLIDER_WIDTH as f32 * 2.0 - 1.0) * PI + pub fn set_show_text_effects(&mut self, show_text_effects: bool) { + self.show_text_effects = show_text_effects; } pub fn update( @@ -118,12 +131,13 @@ where window: &mut W, debug_ui: &mut DebugUI, action: &mut UIAction, + model: &mut DemoUIModel ) where W: Window, { // Draw message text. - self.draw_message_text(device, debug_ui); + self.draw_message_text(device, debug_ui, model); // Draw button strip. @@ -134,10 +148,7 @@ where // Draw text effects button. if self.show_text_effects { - if debug_ui - .ui - .draw_button(device, position, &self.effects_texture) - { + if debug_ui.ui.draw_button(device, position, &self.effects_texture) { self.effects_panel_visible = !self.effects_panel_visible; } if !self.effects_panel_visible { @@ -151,24 +162,16 @@ where } // Draw open button. - if debug_ui - .ui - .draw_button(device, position, &self.open_texture) - { + if debug_ui.ui.draw_button(device, position, &self.open_texture) { // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in // the contents of the file. window.present_open_svg_dialog(); } - debug_ui - .ui - .draw_tooltip(device, "Open SVG", RectI32::new(position, button_size)); + debug_ui.ui.draw_tooltip(device, "Open SVG", RectI32::new(position, button_size)); position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0); // Draw screenshot button. - if debug_ui - .ui - .draw_button(device, position, &self.screenshot_texture) - { + if debug_ui.ui.draw_button(device, position, &self.screenshot_texture) { // FIXME(pcwalton): This is not sufficient for Android, where we will need to take in // the contents of the file. if let Ok(file) = window.run_save_dialog("png") { @@ -184,11 +187,9 @@ where // Draw mode switch. let new_mode = - debug_ui - .ui - .draw_text_switch(device, position, &["2D", "3D", "VR"], self.mode as u8); - if new_mode != self.mode as u8 { - self.mode = match new_mode { + debug_ui.ui.draw_text_switch(device, position, &["2D", "3D", "VR"], model.mode as u8); + if new_mode != model.mode as u8 { + model.mode = match new_mode { 0 => Mode::TwoD, 1 => Mode::ThreeD, _ => Mode::VR, @@ -206,10 +207,7 @@ where position += Point2DI32::new(mode_switch_width + PADDING, 0); // Draw background switch. - if debug_ui - .ui - .draw_button(device, position, &self.background_texture) - { + if debug_ui.ui.draw_button(device, position, &self.background_texture) { self.background_panel_visible = !self.background_panel_visible; } if !self.background_panel_visible { @@ -221,60 +219,48 @@ where } // Draw background panel, if necessary. - self.draw_background_panel(device, debug_ui, position.x(), action); + self.draw_background_panel(device, debug_ui, position.x(), action, model); position += Point2DI32::new(button_size.x() + PADDING, 0); // Draw effects panel, if necessary. - self.draw_effects_panel(device, debug_ui); + self.draw_effects_panel(device, debug_ui, model); // Draw rotate and zoom buttons, if applicable. - if self.mode != Mode::TwoD { + if model.mode != Mode::TwoD { return; } - if debug_ui - .ui - .draw_button(device, position, &self.rotate_texture) - { + if debug_ui.ui.draw_button(device, position, &self.rotate_texture) { self.rotate_panel_visible = !self.rotate_panel_visible; } if !self.rotate_panel_visible { - debug_ui - .ui - .draw_tooltip(device, "Rotate", RectI32::new(position, button_size)); + debug_ui.ui.draw_tooltip(device, "Rotate", RectI32::new(position, button_size)); } - self.draw_rotate_panel(device, debug_ui, position.x(), action); + self.draw_rotate_panel(device, debug_ui, position.x(), action, model); position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0); - if debug_ui - .ui - .draw_button(device, position, &self.zoom_in_texture) - { + if debug_ui.ui.draw_button(device, position, &self.zoom_in_texture) { *action = UIAction::ZoomIn; } - debug_ui - .ui - .draw_tooltip(device, "Zoom In", RectI32::new(position, button_size)); + debug_ui.ui.draw_tooltip(device, "Zoom In", RectI32::new(position, button_size)); position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0); - if debug_ui - .ui - .draw_button(device, position, &self.zoom_out_texture) - { + if debug_ui.ui.draw_button(device, position, &self.zoom_out_texture) { *action = UIAction::ZoomOut; } - debug_ui - .ui - .draw_tooltip(device, "Zoom Out", RectI32::new(position, button_size)); + debug_ui.ui.draw_tooltip(device, "Zoom Out", RectI32::new(position, button_size)); position += Point2DI32::new(BUTTON_WIDTH + PADDING, 0); } - fn draw_message_text(&mut self, device: &D, debug_ui: &mut DebugUI) { - if self.message.is_empty() { + fn draw_message_text(&mut self, + device: &D, + debug_ui: &mut DebugUI, + model: &mut DemoUIModel) { + if model.message.is_empty() { return; } - let message_size = debug_ui.ui.measure_text(&self.message); + let message_size = debug_ui.ui.measure_text(&model.message); let window_origin = Point2DI32::new(PADDING, PADDING); let window_size = Point2DI32::new(PADDING * 2 + message_size, TOOLTIP_HEIGHT); debug_ui.ui.draw_solid_rounded_rect( @@ -284,13 +270,16 @@ where ); debug_ui.ui.draw_text( device, - &self.message, + &model.message, window_origin + Point2DI32::new(PADDING, PADDING + FONT_ASCENT), false, ); } - fn draw_effects_panel(&mut self, device: &D, debug_ui: &mut DebugUI) { + fn draw_effects_panel(&mut self, + device: &D, + debug_ui: &mut DebugUI, + model: &mut DemoUIModel) { if !self.effects_panel_visible { return; } @@ -306,29 +295,29 @@ where WINDOW_COLOR, ); - self.gamma_correction_effect_enabled = self.draw_effects_switch( + model.gamma_correction_effect_enabled = self.draw_effects_switch( device, debug_ui, "Gamma Correction", 0, effects_panel_y, - self.gamma_correction_effect_enabled, + model.gamma_correction_effect_enabled, ); - self.stem_darkening_effect_enabled = self.draw_effects_switch( + model.stem_darkening_effect_enabled = self.draw_effects_switch( device, debug_ui, "Stem Darkening", 1, effects_panel_y, - self.stem_darkening_effect_enabled, + model.stem_darkening_effect_enabled, ); - self.subpixel_aa_effect_enabled = self.draw_effects_switch( + model.subpixel_aa_effect_enabled = self.draw_effects_switch( device, debug_ui, "Subpixel AA", 2, effects_panel_y, - self.subpixel_aa_effect_enabled, + model.subpixel_aa_effect_enabled, ); } @@ -338,6 +327,7 @@ where debug_ui: &mut DebugUI, panel_x: i32, action: &mut UIAction, + model: &mut DemoUIModel, ) { if !self.background_panel_visible { return; @@ -361,6 +351,7 @@ where BackgroundColor::Light, panel_position, action, + model, ); self.draw_background_menu_item( device, @@ -368,6 +359,7 @@ where BackgroundColor::Dark, panel_position, action, + model, ); self.draw_background_menu_item( device, @@ -375,6 +367,7 @@ where BackgroundColor::Transparent, panel_position, action, + model, ); } @@ -384,6 +377,7 @@ where debug_ui: &mut DebugUI, rotate_panel_x: i32, action: &mut UIAction, + model: &mut DemoUIModel ) { if !self.rotate_panel_visible { return; @@ -409,8 +403,8 @@ where .event_queue .handle_mouse_down_or_dragged_in_rect(widget_rect) { - self.rotation = position.x(); - *action = UIAction::Rotate(self.rotation()); + model.rotation = position.x(); + *action = UIAction::Rotate(model.rotation()); } let slider_track_y = @@ -423,14 +417,12 @@ where .ui .draw_rect_outline(device, slider_track_rect, TEXT_COLOR); - let slider_knob_x = widget_x + self.rotation - SLIDER_KNOB_WIDTH / 2; + let slider_knob_x = widget_x + model.rotation - SLIDER_KNOB_WIDTH / 2; let slider_knob_rect = RectI32::new( Point2DI32::new(slider_knob_x, widget_y), Point2DI32::new(SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT), ); - debug_ui - .ui - .draw_solid_rect(device, slider_knob_rect, TEXT_COLOR); + debug_ui.ui.draw_solid_rect(device, slider_knob_rect, TEXT_COLOR); } fn draw_background_menu_item( @@ -440,6 +432,7 @@ where color: BackgroundColor, panel_position: Point2DI32, action: &mut UIAction, + model: &mut DemoUIModel, ) { let (text, index) = (color.as_str(), color as i32); @@ -447,24 +440,16 @@ where let widget_origin = panel_position + Point2DI32::new(0, widget_size.y() * index); let widget_rect = RectI32::new(widget_origin, widget_size); - if color == self.background_color { - debug_ui - .ui - .draw_solid_rounded_rect(device, widget_rect, TEXT_COLOR); + if color == model.background_color { + debug_ui.ui.draw_solid_rounded_rect(device, widget_rect, TEXT_COLOR); } let (text_x, text_y) = (PADDING * 2, BUTTON_TEXT_OFFSET); let text_position = widget_origin + Point2DI32::new(text_x, text_y); - debug_ui - .ui - .draw_text(device, text, text_position, color == self.background_color); + debug_ui.ui.draw_text(device, text, text_position, color == model.background_color); - if let Some(_) = debug_ui - .ui - .event_queue - .handle_mouse_down_in_rect(widget_rect) - { - self.background_color = color; + if debug_ui.ui.event_queue.handle_mouse_down_in_rect(widget_rect).is_some() { + model.background_color = color; *action = UIAction::ModelChanged; } }