Add Widget trait, improve scrollbar

This commit is contained in:
Adrian Wozniak 2019-12-21 18:45:56 +01:00
parent 9b6ef389b9
commit a0d7f4acec
11 changed files with 380 additions and 333 deletions

View File

@ -1,8 +1,5 @@
use crate::app::{ConfigAccess, ConfigHolder, UpdateResult as UR}; use crate::app::{ConfigAccess, UpdateResult as UR};
use crate::renderer::Renderer; use crate::ui::{UpdateContext, Widget, WidgetInner};
use crate::ui::{
move_render_point, CanvasAccess, ClickHandler, RenderBox, RenderContext, Update, UpdateContext,
};
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
const ICON_DEST_WIDTH: u32 = 16; const ICON_DEST_WIDTH: u32 = 16;
@ -11,97 +8,69 @@ const ICON_SRC_WIDTH: u32 = 32;
const ICON_SRC_HEIGHT: u32 = 32; const ICON_SRC_HEIGHT: u32 = 32;
pub struct SaveButton { pub struct SaveButton {
source: Rect, inner: WidgetInner,
dest: Rect, }
config: ConfigAccess,
impl std::ops::Deref for SaveButton {
type Target = WidgetInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for SaveButton {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Widget for SaveButton {
fn texture_path(&self) -> Option<String> {
let c = self.config.read().unwrap();
let mut themes_dir = c.directories().themes_dir.clone();
let path = c.theme().images().save_icon();
themes_dir.push(path);
Some(themes_dir.to_str().unwrap().to_owned())
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.inner.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.source
}
fn set_source(&mut self, rect: &Rect) {
self.inner.source = rect.clone();
}
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::SaveCurrentFile
}
fn padding_width(&self) -> u32 {
ICON_DEST_WIDTH
}
fn padding_height(&self) -> u32 {
ICON_DEST_HEIGHT
}
} }
impl SaveButton { impl SaveButton {
pub fn new(config: ConfigAccess) -> Self { pub fn new(config: ConfigAccess) -> Self {
Self { Self {
dest: Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT), inner: WidgetInner::new(
source: Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
config, config,
Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
),
} }
} }
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
{
use std::borrow::*;
let mut dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
let mut clipping = dest.clone();
clipping.set_width(clipping.width() + ICON_DEST_WIDTH);
clipping.set_height(clipping.height() + ICON_DEST_HEIGHT);
canvas.set_clipping(clipping);
let save_texture_path = {
let c = self.config.read().unwrap();
let mut themes_dir = c.directories().themes_dir.clone();
let path = c.theme().images().save_icon();
themes_dir.push(path);
themes_dir.to_str().unwrap().to_owned()
};
let maybe_tex = renderer.load_image(save_texture_path.clone());
if let Ok(texture) = maybe_tex {
dest.set_width(ICON_DEST_WIDTH);
dest.set_height(ICON_DEST_HEIGHT);
canvas
.render_image(texture, self.source.clone(), dest.clone())
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
}
}
pub fn prepare_ui<'l, T>(&mut self, _renderer: &mut T)
where
T: ConfigHolder + Renderer,
{
}
pub fn source(&self) -> &Rect {
&self.source
}
pub fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
pub fn set_source(&mut self, rect: &Rect) {
self.source = rect.clone();
}
}
impl Update for SaveButton {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR {
UR::NoOp
}
}
impl ClickHandler for SaveButton {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::SaveCurrentFile
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest,
}
.contains_point(point.clone())
}
}
impl RenderBox for SaveButton {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest
}
} }

View File

@ -1,8 +1,6 @@
use crate::app::{ConfigAccess, ConfigHolder, UpdateResult as UR}; use crate::app::UpdateResult as UR;
use crate::renderer::Renderer; use crate::ui::{UpdateContext, Widget, WidgetInner};
use crate::ui::{ use rider_config::ConfigAccess;
move_render_point, CanvasAccess, ClickHandler, RenderBox, RenderContext, Update, UpdateContext,
};
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
const ICON_DEST_WIDTH: u32 = 16; const ICON_DEST_WIDTH: u32 = 16;
@ -11,97 +9,69 @@ const ICON_SRC_WIDTH: u32 = 16;
const ICON_SRC_HEIGHT: u32 = 16; const ICON_SRC_HEIGHT: u32 = 16;
pub struct SettingsButton { pub struct SettingsButton {
source: Rect, inner: WidgetInner,
dest: Rect, }
config: ConfigAccess,
impl std::ops::Deref for SettingsButton {
type Target = WidgetInner;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl std::ops::DerefMut for SettingsButton {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl Widget for SettingsButton {
fn texture_path(&self) -> Option<String> {
let c = self.config.read().unwrap();
let mut themes_dir = c.directories().themes_dir.clone();
let path = c.theme().images().settings_icon();
themes_dir.push(path);
Some(themes_dir.to_str().unwrap().to_owned())
}
fn dest(&self) -> &Rect {
&self.dest
}
fn set_dest(&mut self, rect: &Rect) {
self.inner.dest = rect.clone();
}
fn source(&self) -> &Rect {
&self.source
}
fn set_source(&mut self, rect: &Rect) {
self.inner.source = rect.clone();
}
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::OpenSettings
}
fn padding_width(&self) -> u32 {
ICON_DEST_WIDTH
}
fn padding_height(&self) -> u32 {
ICON_DEST_HEIGHT
}
} }
impl SettingsButton { impl SettingsButton {
pub fn new(config: ConfigAccess) -> Self { pub fn new(config: ConfigAccess) -> Self {
Self { Self {
dest: Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT), inner: WidgetInner::new(
source: Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
config, config,
Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
),
} }
} }
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
{
use std::borrow::*;
let mut dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest.clone(),
};
let mut clipping = dest.clone();
clipping.set_width(clipping.width() + ICON_DEST_WIDTH);
clipping.set_height(clipping.height() + ICON_DEST_HEIGHT);
canvas.set_clipping(clipping);
let settings_texture_path = {
let c = self.config.read().unwrap();
let mut themes_dir = c.directories().themes_dir.clone();
let path = c.theme().images().settings_icon();
themes_dir.push(path);
themes_dir.to_str().unwrap().to_owned()
};
let maybe_tex = renderer.load_image(settings_texture_path.clone());
if let Ok(texture) = maybe_tex {
dest.set_width(ICON_DEST_WIDTH);
dest.set_height(ICON_DEST_HEIGHT);
canvas
.render_image(texture, self.source.clone(), dest.clone())
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
}
}
pub fn prepare_ui<'l, T>(&mut self, _renderer: &mut T)
where
T: ConfigHolder + Renderer,
{
}
pub fn source(&self) -> &Rect {
&self.source
}
pub fn set_dest(&mut self, rect: &Rect) {
self.dest = rect.clone();
}
pub fn set_source(&mut self, rect: &Rect) {
self.source = rect.clone();
}
}
impl Update for SettingsButton {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR {
UR::NoOp
}
}
impl ClickHandler for SettingsButton {
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UR {
UR::OpenSettings
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
_ => self.dest,
}
.contains_point(point.clone())
}
}
impl RenderBox for SettingsButton {
fn render_start_point(&self) -> Point {
self.dest.top_left()
}
fn dest(&self) -> Rect {
self.dest
}
} }

View File

@ -9,7 +9,7 @@ use crate::ui::file::TextCollection;
use crate::ui::file::TextWidget; use crate::ui::file::TextWidget;
use crate::ui::scroll_bar::horizontal_scroll_bar::*; use crate::ui::scroll_bar::horizontal_scroll_bar::*;
use crate::ui::scroll_bar::vertical_scroll_bar::*; use crate::ui::scroll_bar::vertical_scroll_bar::*;
use crate::ui::scroll_bar::Scroll; use crate::ui::scroll_bar::ScrollWidget;
use crate::ui::text_character::CharacterSizeManager; use crate::ui::text_character::CharacterSizeManager;
use crate::ui::CanvasAccess; use crate::ui::CanvasAccess;
use crate::ui::ClickHandler; use crate::ui::ClickHandler;
@ -175,8 +175,8 @@ impl FileAccess for FileEditor {
if let Some(f) = self.file.as_ref() { if let Some(f) = self.file.as_ref() {
self.full_rect = f.full_rect(); self.full_rect = f.full_rect();
} }
self.vertical_scroll_bar.set_location(0); self.vertical_scroll_bar.reset();
self.horizontal_scroll_bar.set_location(0); self.horizontal_scroll_bar.reset();
file file
} }

View File

@ -71,15 +71,21 @@ impl MenuBar {
) )
.unwrap_or_else(|_| panic!("Failed to draw main menu background")); .unwrap_or_else(|_| panic!("Failed to draw main menu background"));
let context = RenderContext::ParentPosition( self.save_button.render(
canvas,
renderer,
&RenderContext::ParentPosition(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP), relative_position.offset(SAVE_BUTTON_OFFSET_LEFT, SAVE_BUTTON_OFFSET_TOP),
),
); );
self.save_button.render(canvas, renderer, &context);
let context = RenderContext::ParentPosition( self.settings_button.render(
canvas,
renderer,
&RenderContext::ParentPosition(
relative_position.offset(SAVE_BUTTON_OFFSET_LEFT * 2, SAVE_BUTTON_OFFSET_TOP), relative_position.offset(SAVE_BUTTON_OFFSET_LEFT * 2, SAVE_BUTTON_OFFSET_TOP),
),
); );
self.settings_button.render(canvas, renderer, &context);
} }
pub fn prepare_ui(&mut self) { pub fn prepare_ui(&mut self) {

View File

@ -4,7 +4,7 @@ use sdl2::render::Texture;
use std::rc::Rc; use std::rc::Rc;
use crate::app::application::WindowCanvas; use crate::app::application::WindowCanvas;
use crate::app::UpdateResult as UR; use crate::app::{UpdateResult as UR, UpdateResult};
use crate::renderer::managers::*; use crate::renderer::managers::*;
use rider_config::*; use rider_config::*;
@ -31,6 +31,7 @@ pub use self::modal::*;
pub use self::project_tree::*; pub use self::project_tree::*;
pub use self::scroll_bar::*; pub use self::scroll_bar::*;
pub use self::text_character::*; pub use self::text_character::*;
use crate::renderer::Renderer;
#[derive(Debug)] #[derive(Debug)]
pub enum UpdateContext<'l> { pub enum UpdateContext<'l> {
@ -140,6 +141,100 @@ pub trait RenderBox {
fn dest(&self) -> Rect; fn dest(&self) -> Rect;
} }
pub struct WidgetInner {
source: Rect,
dest: Rect,
config: ConfigAccess,
}
impl WidgetInner {
pub fn new(config: ConfigAccess, source: Rect, dest: Rect) -> Self {
Self {
dest,
source,
config,
}
}
}
pub trait Widget {
fn texture_path(&self) -> Option<String>;
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
where
C: CanvasAccess,
R: Renderer,
{
let mut dest = match context {
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest()),
_ => self.dest().clone(),
};
canvas.set_clipping(self.clipping(&dest));
self.texture_path()
.and_then(|path| renderer.load_image(path).ok())
.and_then(|texture| {
dest.set_width(self.dest().width());
dest.set_height(self.dest().height());
canvas
.render_image(texture.clone(), self.source().clone(), dest.clone())
.unwrap_or_else(|_| panic!("Failed to draw widget texture"));
Some(())
});
}
fn prepare_ui<'l, T>(&mut self, _renderer: &mut T)
where
T: ConfigHolder + Renderer,
{
}
fn dest(&self) -> &Rect;
fn set_dest(&mut self, rect: &Rect);
fn source(&self) -> &Rect;
fn set_source(&mut self, rect: &Rect);
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
UpdateResult::NoOp
}
fn on_left_click(&mut self, _point: &Point, _context: &UpdateContext) -> UpdateResult {
UpdateResult::NoOp
}
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
match *context {
UpdateContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest()),
_ => self.dest().clone(),
}
.contains_point(point.clone())
}
fn render_start_point(&self) -> Point {
self.dest().top_left()
}
fn clipping(&self, relative_dest: &Rect) -> Rect {
Rect::new(
relative_dest.x(),
relative_dest.y(),
relative_dest.width() + self.padding_width(),
relative_dest.height() + self.padding_height(),
)
}
fn padding_width(&self) -> u32 {
0
}
fn padding_height(&self) -> u32 {
0
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -24,6 +24,30 @@ pub struct OpenFile {
config: ConfigAccess, config: ConfigAccess,
} }
impl ScrollView<VerticalScrollBar, HorizontalScrollBar> for OpenFile {
fn mut_horizontal_scroll_handler(&mut self) -> Option<&mut HorizontalScrollBar> {
Some(&mut self.horizontal_scroll_bar)
}
fn horizontal_scroll_handler(&self) -> Option<&HorizontalScrollBar> {
Some(&self.horizontal_scroll_bar)
}
fn mut_vertical_scroll_handler(&mut self) -> Option<&mut VerticalScrollBar> {
Some(&mut self.vertical_scroll_bar)
}
fn vertical_scroll_handler(&self) -> Option<&VerticalScrollBar> {
Some(&self.vertical_scroll_bar)
}
}
impl ConfigHolder for OpenFile {
fn config(&self) -> &ConfigAccess {
&self.config
}
}
impl OpenFile { impl OpenFile {
pub fn new(root_path: String, width: u32, height: u32, config: ConfigAccess) -> Self { pub fn new(root_path: String, width: u32, height: u32, config: ConfigAccess) -> Self {
let (window_width, window_height, background_color, border_color) = { let (window_width, window_height, background_color, border_color) = {
@ -79,35 +103,6 @@ impl OpenFile {
&self.full_dest &self.full_dest
} }
pub fn scroll_by(&mut self, x: i32, y: i32) {
let read_config = self.config.read().unwrap();
let value_x = read_config.scroll().speed() * x;
let value_y = read_config.scroll().speed() * y;
let old_x = self.horizontal_scroll_bar.scroll_value();
let old_y = self.vertical_scroll_bar.scroll_value();
if value_x + old_x >= 0 {
self.horizontal_scroll_bar.scroll_to(value_x + old_x);
if self.horizontal_scroll_bar.scrolled_part() > 1.0 {
self.horizontal_scroll_bar.scroll_to(old_x);
}
}
if value_y + old_y >= 0 {
self.vertical_scroll_bar.scroll_to(value_y + old_y);
if self.vertical_scroll_bar.scrolled_part() > 1.0 {
self.vertical_scroll_bar.scroll_to(old_y);
}
}
}
pub fn scroll(&self) -> Point {
Point::new(
-self.horizontal_scroll_bar.scroll_value(),
-self.vertical_scroll_bar.scroll_value(),
)
}
pub fn update(&mut self, ticks: i32, context: &UC) -> UR { pub fn update(&mut self, ticks: i32, context: &UC) -> UR {
let (window_width, window_height, color, scroll_width, scroll_margin) = { let (window_width, window_height, color, scroll_width, scroll_margin) = {
let c = self.config.read().unwrap(); let c = self.config.read().unwrap();
@ -218,8 +213,8 @@ impl OpenFile {
pub fn is_left_click_target(&self, point: &Point, context: &UC) -> bool { pub fn is_left_click_target(&self, point: &Point, context: &UC) -> bool {
let dest = match context { let dest = match context {
UC::ParentPosition(p) => move_render_point(p.clone(), &self.dest), UC::ParentPosition(p) => move_render_point(p.clone(), &self.dest()),
_ => self.dest.clone(), _ => self.dest().clone(),
}; };
let p = let p =
dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll(); dest.top_left() + Point::new(CONTENT_MARGIN_LEFT, CONTENT_MARGIN_TOP) + self.scroll();
@ -248,7 +243,7 @@ mod tests {
let config = build_config(); let config = build_config();
let mut widget = OpenFile::new("/tmp".to_owned(), 100, 100, config); let mut widget = OpenFile::new("/tmp".to_owned(), 100, 100, config);
widget.scroll_by(12, 13); widget.scroll_by(12, 13);
assert_eq!(widget.scroll(), Point::new(0, -390)); assert_eq!(widget.scroll(), Point::new(-360, -390));
} }
//####################################################################### //#######################################################################

View File

@ -311,7 +311,7 @@ mod tests {
let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config); let mut widget = ProjectTreeSidebar::new("/tmp".to_owned(), config);
widget.scroll_by(10, 10); widget.scroll_by(10, 10);
let res = widget.scroll(); let res = widget.scroll();
let expected = Point::new(0, -300); let expected = Point::new(-300, -300);
assert_eq!(res, expected); assert_eq!(res, expected);
} }

View File

@ -1,7 +1,5 @@
use crate::app::UpdateResult as UR;
use crate::ui::*; use crate::ui::*;
use rider_config::ConfigAccess; use rider_config::ConfigAccess;
use sdl2::pixels::Color;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub struct HorizontalScrollBar { pub struct HorizontalScrollBar {
@ -22,6 +20,7 @@ impl DerefMut for HorizontalScrollBar {
} }
} }
#[cfg_attr(tarpaulin, skip)]
impl HorizontalScrollBar { impl HorizontalScrollBar {
pub fn new(config: ConfigAccess) -> Self { pub fn new(config: ConfigAccess) -> Self {
Self { Self {
@ -30,70 +29,55 @@ impl HorizontalScrollBar {
} }
} }
impl Update for HorizontalScrollBar { impl ScrollWidget for HorizontalScrollBar {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR { fn update_rect(&mut self, pos: i32, max: u32) {
if self.full < self.viewport { self.mut_rect().set_width(max);
return UR::NoOp; self.mut_rect().set_x(pos);
}
let ratio = self.full as f64 / self.viewport as f64;
let width = (self.viewport as f64 / ratio) as u32;
self.rect.set_width(width);
let x = (self.viewport - self.rect.width()) as f64
* (self.scroll_value().abs() as f64 / (self.full - self.viewport) as f64);
self.rect.set_x(x as i32);
UR::NoOp
}
}
#[cfg_attr(tarpaulin, skip)]
impl HorizontalScrollBar {
pub fn render<T>(&self, canvas: &mut T, context: &RenderContext)
where
T: CanvasAccess,
{
if self.full < self.viewport {
return;
} }
canvas #[inline]
.render_rect(
match context {
RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.rect),
_ => self.rect.clone(),
},
Color::RGBA(255, 255, 255, 0),
)
.unwrap_or_else(|_| panic!("Failed to render vertical scroll back"));
}
}
impl Scroll for HorizontalScrollBar {
fn scroll_to(&mut self, n: i32) { fn scroll_to(&mut self, n: i32) {
self.scroll_value = n; self.scroll_value = n;
} }
#[inline]
fn scroll_value(&self) -> i32 { fn scroll_value(&self) -> i32 {
self.scroll_value self.scroll_value
} }
#[inline]
fn set_viewport(&mut self, n: u32) { fn set_viewport(&mut self, n: u32) {
self.viewport = n; self.viewport = n;
} }
#[inline]
fn set_full_size(&mut self, n: u32) { fn set_full_size(&mut self, n: u32) {
self.full = n; self.full = n;
} }
#[inline]
fn set_location(&mut self, n: i32) { fn set_location(&mut self, n: i32) {
self.rect.set_y(n); self.rect.set_y(n);
} }
fn scrolled_part(&self) -> f64 { #[inline]
if self.full < self.viewport() { fn viewport(&self) -> u32 {
return 1.0; self.viewport
} }
self.scroll_value().abs() as f64 / (self.full - self.viewport()) as f64
#[inline]
fn full(&self) -> u32 {
self.full
}
#[inline]
fn rect(&self) -> &sdl2::rect::Rect {
&self.rect
}
#[inline]
fn mut_rect(&mut self) -> &mut sdl2::rect::Rect {
&mut self.rect
} }
} }
@ -104,7 +88,7 @@ mod test_update {
use std::sync::*; use std::sync::*;
impl HorizontalScrollBar { impl HorizontalScrollBar {
pub fn rect_mut(&mut self) -> &mut Rect { pub fn rect_mut(&mut self) -> &mut sdl2::rect::Rect {
&mut self.rect &mut self.rect
} }
} }

View File

@ -1,16 +1,19 @@
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
use crate::app::UpdateResult;
pub use crate::ui::scroll_bar::horizontal_scroll_bar::HorizontalScrollBar; pub use crate::ui::scroll_bar::horizontal_scroll_bar::HorizontalScrollBar;
pub use crate::ui::scroll_bar::vertical_scroll_bar::VerticalScrollBar; pub use crate::ui::scroll_bar::vertical_scroll_bar::VerticalScrollBar;
use crate::ui::{move_render_point, CanvasAccess, RenderContext, UpdateContext};
use rider_config::{ConfigAccess, ConfigHolder}; use rider_config::{ConfigAccess, ConfigHolder};
use sdl2::pixels::Color;
pub mod horizontal_scroll_bar; pub mod horizontal_scroll_bar;
pub mod vertical_scroll_bar; pub mod vertical_scroll_bar;
pub trait ScrollView<VS, HS>: ConfigHolder pub trait ScrollView<VS, HS>: ConfigHolder
where where
VS: Scroll, VS: ScrollWidget,
HS: Scroll, HS: ScrollWidget,
{ {
fn scroll_by(&mut self, x: i32, y: i32) { fn scroll_by(&mut self, x: i32, y: i32) {
let speed = self.config().read().unwrap().scroll().speed(); let speed = self.config().read().unwrap().scroll().speed();
@ -70,7 +73,39 @@ where
fn vertical_scroll_handler(&self) -> Option<&VS>; fn vertical_scroll_handler(&self) -> Option<&VS>;
} }
pub trait Scroll { pub trait ScrollWidget {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
if self.full() < self.viewport() {
return UpdateResult::NoOp;
}
let max = (self.viewport() as f64 / self.ratio()) as u32;
let pos = (self.viewport() - max) as f64 * self.scrolled_part();
self.update_rect(pos as i32, max);
UpdateResult::NoOp
}
fn render<T>(&self, canvas: &mut T, context: &RenderContext)
where
T: CanvasAccess,
{
if self.full() < self.viewport() {
return;
}
canvas
.render_border(
match context {
RenderContext::ParentPosition(p) => move_render_point(p.clone(), self.rect()),
_ => self.rect().clone(),
},
Color::RGBA(255, 255, 255, 0),
)
.unwrap_or_else(|_| panic!("Failed to render vertical scroll back"));
}
fn update_rect(&mut self, pos: i32, max: u32);
fn scroll_to(&mut self, n: i32); fn scroll_to(&mut self, n: i32);
fn scroll_value(&self) -> i32; fn scroll_value(&self) -> i32;
@ -81,7 +116,31 @@ pub trait Scroll {
fn set_location(&mut self, n: i32); fn set_location(&mut self, n: i32);
fn scrolled_part(&self) -> f64; #[inline]
fn scrolled_part(&self) -> f64 {
if self.full() <= self.viewport() {
return 1.0;
}
self.scroll_value().abs() as f64 / (self.full() - self.viewport()) as f64
}
fn viewport(&self) -> u32;
fn full(&self) -> u32;
fn rect(&self) -> &Rect;
fn mut_rect(&mut self) -> &mut Rect;
fn ratio(&self) -> f64 {
self.full() as f64 / self.viewport() as f64
}
fn reset(&mut self) {
self.update_rect(0, 0);
self.set_full_size(0);
self.scroll_to(0);
}
} }
pub struct ScrollBar { pub struct ScrollBar {
@ -101,19 +160,4 @@ impl ScrollBar {
rect: Rect::new(0, 0, width, 0), rect: Rect::new(0, 0, width, 0),
} }
} }
#[inline]
pub fn viewport(&self) -> u32 {
self.viewport
}
#[inline]
pub fn full(&self) -> u32 {
self.full
}
#[inline]
pub fn rect(&self) -> &Rect {
&self.rect
}
} }

View File

@ -1,7 +1,5 @@
use crate::app::UpdateResult as UR;
use crate::ui::*; use crate::ui::*;
use rider_config::ConfigAccess; use rider_config::ConfigAccess;
use sdl2::pixels::Color;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
pub struct VerticalScrollBar { pub struct VerticalScrollBar {
@ -22,6 +20,7 @@ impl DerefMut for VerticalScrollBar {
} }
} }
#[cfg_attr(tarpaulin, skip)]
impl VerticalScrollBar { impl VerticalScrollBar {
pub fn new(config: ConfigAccess) -> Self { pub fn new(config: ConfigAccess) -> Self {
Self { Self {
@ -30,69 +29,55 @@ impl VerticalScrollBar {
} }
} }
impl Update for VerticalScrollBar { impl ScrollWidget for VerticalScrollBar {
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UR { fn update_rect(&mut self, pos: i32, max: u32) {
if self.full < self.viewport() { self.mut_rect().set_height(max);
return UR::NoOp; self.mut_rect().set_y(pos);
}
let ratio = self.full as f64 / self.viewport() as f64;
let height = (self.viewport() as f64 / ratio) as u32;
self.rect.set_height(height);
let y = (self.viewport() - self.rect.height()) as f64 * self.scrolled_part();
self.rect.set_y(y as i32);
UR::NoOp
}
}
#[cfg_attr(tarpaulin, skip)]
impl VerticalScrollBar {
pub fn render<T>(&self, canvas: &mut T, context: &RenderContext)
where
T: CanvasAccess,
{
if self.full < self.viewport() {
return;
} }
canvas #[inline]
.render_border(
match context {
RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.rect),
_ => self.rect.clone(),
},
Color::RGBA(255, 255, 255, 0),
)
.unwrap_or_else(|_| panic!("Failed to render vertical scroll back"));
}
}
impl Scroll for VerticalScrollBar {
fn scroll_to(&mut self, n: i32) { fn scroll_to(&mut self, n: i32) {
self.scroll_value = n; self.scroll_value = n;
} }
#[inline]
fn scroll_value(&self) -> i32 { fn scroll_value(&self) -> i32 {
self.scroll_value self.scroll_value
} }
#[inline]
fn set_viewport(&mut self, n: u32) { fn set_viewport(&mut self, n: u32) {
self.viewport = n; self.viewport = n;
} }
#[inline]
fn set_full_size(&mut self, n: u32) { fn set_full_size(&mut self, n: u32) {
self.full = n; self.full = n;
} }
#[inline]
fn set_location(&mut self, n: i32) { fn set_location(&mut self, n: i32) {
self.rect.set_x(n); self.rect.set_x(n);
} }
fn scrolled_part(&self) -> f64 { #[inline]
if self.full <= self.viewport() { fn viewport(&self) -> u32 {
return 1.0; self.viewport
} }
self.scroll_value().abs() as f64 / (self.full - self.viewport()) as f64
#[inline]
fn full(&self) -> u32 {
self.full
}
#[inline]
fn rect(&self) -> &sdl2::rect::Rect {
&self.rect
}
#[inline]
fn mut_rect(&mut self) -> &mut sdl2::rect::Rect {
&mut self.rect
} }
} }

View File

@ -269,5 +269,4 @@ mod tests {
let expected = ThemeConfig::new(SerdeColor::new(0, 0, 0, 0), false, false); let expected = ThemeConfig::new(SerdeColor::new(0, 0, 0, 0), false, false);
assert_eq!(result, expected); assert_eq!(result, expected);
} }
} }