Simplify components
This commit is contained in:
parent
a0d7f4acec
commit
a80b3d0107
@ -1,7 +1,7 @@
|
|||||||
use crate::app::*;
|
use crate::app::*;
|
||||||
use crate::renderer::*;
|
use crate::renderer::*;
|
||||||
|
use crate::ui::icon::Icon;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use sdl2::pixels::Color;
|
|
||||||
use sdl2::rect::{Point, Rect};
|
use sdl2::rect::{Point, Rect};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path;
|
use std::path;
|
||||||
@ -11,35 +11,234 @@ const CHILD_MARGIN: i32 = 4;
|
|||||||
const DEFAULT_ICON_SIZE: u32 = 16;
|
const DEFAULT_ICON_SIZE: u32 = 16;
|
||||||
|
|
||||||
pub struct DirectoryView {
|
pub struct DirectoryView {
|
||||||
|
inner: WidgetInner,
|
||||||
opened: bool,
|
opened: bool,
|
||||||
expanded: bool,
|
expanded: bool,
|
||||||
name_width: u32,
|
|
||||||
icon_width: u32,
|
|
||||||
icon_height: u32,
|
|
||||||
height: u32,
|
height: u32,
|
||||||
path: String,
|
path: String,
|
||||||
files: Vec<FileEntry>,
|
files: Vec<FileEntry>,
|
||||||
directories: Vec<DirectoryView>,
|
directories: Vec<DirectoryView>,
|
||||||
pos: Point,
|
name_label: Label,
|
||||||
source: Rect,
|
icon: Icon,
|
||||||
config: ConfigAccess,
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for DirectoryView {
|
||||||
|
type Target = WidgetInner;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for DirectoryView {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for DirectoryView {
|
||||||
|
fn texture_path(&self) -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dest(&self) -> &Rect {
|
||||||
|
&self.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dest(&mut self, _rect: &Rect) {}
|
||||||
|
|
||||||
|
fn source(&self) -> &Rect {
|
||||||
|
&self.inner.source
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_source(&mut self, _rect: &Rect) {}
|
||||||
|
|
||||||
|
fn update(&mut self, ticks: i32, context: &UpdateContext) -> UpdateResult {
|
||||||
|
self.icon.update(ticks, context);
|
||||||
|
self.name_label.update(ticks, context);
|
||||||
|
if !path::Path::new(&self.path).exists() {
|
||||||
|
return UpdateResult::RefreshFsTree;
|
||||||
|
}
|
||||||
|
if self.opened {
|
||||||
|
for dir in self.directories.iter_mut() {
|
||||||
|
dir.update(ticks, context);
|
||||||
|
}
|
||||||
|
for file in self.files.iter_mut() {
|
||||||
|
file.update(ticks, context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UpdateResult::NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UpdateResult {
|
||||||
|
let dest = self.dest();
|
||||||
|
let move_point = match context {
|
||||||
|
&UpdateContext::ParentPosition(p) => p.clone(),
|
||||||
|
_ => Point::new(0, 0),
|
||||||
|
};
|
||||||
|
let dest = move_render_point(move_point.clone(), &dest);
|
||||||
|
|
||||||
|
// icon or name is target of click
|
||||||
|
let icon_or_name = self.name_and_icon_rect();
|
||||||
|
if move_render_point(move_point, &icon_or_name).contains_point(point.clone()) {
|
||||||
|
return UpdateResult::OpenDirectory(self.path.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.expanded {
|
||||||
|
return UpdateResult::NoOp;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut p = dest.top_left()
|
||||||
|
+ Point::new(
|
||||||
|
self.icon_width() as i32 + CHILD_MARGIN,
|
||||||
|
self.icon_height() as i32 + CHILD_MARGIN,
|
||||||
|
);
|
||||||
|
for dir in self.directories.iter_mut() {
|
||||||
|
let context = UpdateContext::ParentPosition(p.clone());
|
||||||
|
if dir.is_left_click_target(&point, &context) {
|
||||||
|
return dir.on_left_click(&point, &context);
|
||||||
|
}
|
||||||
|
p = p + Point::new(0, dir.height() as i32 + CHILD_MARGIN);
|
||||||
|
}
|
||||||
|
for file in self.files.iter_mut() {
|
||||||
|
let context = UpdateContext::ParentPosition(p.clone());
|
||||||
|
if file.is_left_click_target(&point, &context) {
|
||||||
|
return file.on_left_click();
|
||||||
|
}
|
||||||
|
p = p + Point::new(0, file.height() as i32 + CHILD_MARGIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateResult::NoOp
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
|
||||||
|
let dest = self.dest();
|
||||||
|
let move_point = match context {
|
||||||
|
UpdateContext::ParentPosition(p) => p.clone(),
|
||||||
|
_ => Point::new(0, 0),
|
||||||
|
};
|
||||||
|
let dest = move_render_point(move_point.clone(), &dest);
|
||||||
|
|
||||||
|
// icon or name is target of click
|
||||||
|
let name_and_icon_rect = self.name_and_icon_rect();
|
||||||
|
if move_render_point(move_point.clone(), &name_and_icon_rect).contains_point(point.clone())
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if !self.expanded {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut p = dest.top_left()
|
||||||
|
+ Point::new(
|
||||||
|
self.icon_width() as i32 + CHILD_MARGIN,
|
||||||
|
self.icon_height() as i32 + CHILD_MARGIN,
|
||||||
|
);
|
||||||
|
// subdirectory is target of click
|
||||||
|
for dir in self.directories.iter() {
|
||||||
|
let context = UpdateContext::ParentPosition(p.clone());
|
||||||
|
if dir.is_left_click_target(&point, &context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
p = p + Point::new(0, dir.height() as i32 + CHILD_MARGIN);
|
||||||
|
}
|
||||||
|
// file inside directory is target of click
|
||||||
|
for file in self.files.iter() {
|
||||||
|
let context = UpdateContext::ParentPosition(p.clone());
|
||||||
|
if file.is_left_click_target(&point, &context) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
p = p + Point::new(0, file.height() as i32 + CHILD_MARGIN);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
||||||
|
where
|
||||||
|
R: Renderer + CharacterSizeManager,
|
||||||
|
C: CanvasAccess,
|
||||||
|
{
|
||||||
|
let mut dest = move_render_point(
|
||||||
|
match context {
|
||||||
|
&RenderContext::ParentPosition(p) => p.clone(),
|
||||||
|
_ => Point::new(0, 0),
|
||||||
|
},
|
||||||
|
self.dest(),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.icon.render(
|
||||||
|
canvas,
|
||||||
|
renderer,
|
||||||
|
&RenderContext::ParentPosition(Point::new(dest.x(), dest.y())),
|
||||||
|
);
|
||||||
|
self.name_label.render(
|
||||||
|
canvas,
|
||||||
|
renderer,
|
||||||
|
&RenderContext::ParentPosition(Point::new(
|
||||||
|
dest.x() + self.icon_width() as i32,
|
||||||
|
dest.y(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
self.render_children::<C, R>(canvas, renderer, &mut dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_ui<R>(&mut self, renderer: &mut R)
|
||||||
|
where
|
||||||
|
R: Renderer + CharacterSizeManager,
|
||||||
|
{
|
||||||
|
let size = renderer.load_character_size('W');
|
||||||
|
self.icon.prepare_ui(renderer);
|
||||||
|
self.icon.dest.set_height(size.height());
|
||||||
|
self.icon.dest.set_width(size.height());
|
||||||
|
|
||||||
|
self.name_label.prepare_ui(renderer);
|
||||||
|
if self.opened {
|
||||||
|
for dir in self.directories.iter_mut() {
|
||||||
|
dir.prepare_ui(renderer);
|
||||||
|
}
|
||||||
|
for file in self.files.iter_mut() {
|
||||||
|
file.prepare_ui(renderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.calculate_size(renderer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirectoryView {
|
impl DirectoryView {
|
||||||
pub fn new(path: String, config: ConfigAccess) -> Self {
|
pub fn new(path: String, config: ConfigAccess) -> Self {
|
||||||
|
let dir_texture_path = {
|
||||||
|
let c = config.read().unwrap();
|
||||||
|
let mut themes_dir = c.directories().themes_dir.clone();
|
||||||
|
let path = c.theme().images().directory_icon();
|
||||||
|
themes_dir.push(path);
|
||||||
|
themes_dir.to_str().unwrap().to_owned()
|
||||||
|
};
|
||||||
|
|
||||||
|
let name = std::path::Path::new(&path)
|
||||||
|
.file_name()
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned();
|
||||||
Self {
|
Self {
|
||||||
opened: false,
|
opened: false,
|
||||||
expanded: false,
|
expanded: false,
|
||||||
name_width: 0,
|
|
||||||
icon_width: DEFAULT_ICON_SIZE,
|
|
||||||
icon_height: DEFAULT_ICON_SIZE,
|
|
||||||
height: 0,
|
height: 0,
|
||||||
path,
|
path,
|
||||||
files: vec![],
|
files: vec![],
|
||||||
directories: vec![],
|
directories: vec![],
|
||||||
pos: Point::new(0, 0),
|
inner: WidgetInner::new(
|
||||||
source: Rect::new(0, 0, 64, 64),
|
config.clone(),
|
||||||
|
Rect::new(0, 0, 64, 64),
|
||||||
|
Rect::new(0, 0, 0, 0),
|
||||||
|
),
|
||||||
|
name_label: Label::new(name, config.clone()),
|
||||||
|
icon: Icon::new(
|
||||||
config,
|
config,
|
||||||
|
dir_texture_path,
|
||||||
|
Rect::new(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE),
|
||||||
|
Rect::new(0, 0, DEFAULT_ICON_SIZE, DEFAULT_ICON_SIZE),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,34 +246,24 @@ impl DirectoryView {
|
|||||||
self.path.clone()
|
self.path.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dest(&self) -> Rect {
|
fn expand_view(&mut self) {
|
||||||
match self.expanded {
|
self.expanded = true;
|
||||||
true => Rect::new(
|
self.dest = Rect::new(
|
||||||
self.pos.x(),
|
self.dest.x(),
|
||||||
self.pos.y(),
|
self.dest.y(),
|
||||||
self.icon_width + self.name_width + NAME_MARGIN as u32,
|
self.icon_width() + self.name_width() + NAME_MARGIN as u32,
|
||||||
self.height,
|
self.height,
|
||||||
),
|
);
|
||||||
false => Rect::new(
|
|
||||||
self.pos.x(),
|
|
||||||
self.pos.y(),
|
|
||||||
self.icon_width + self.name_width + NAME_MARGIN as u32,
|
|
||||||
self.icon_height,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn dest(&self) -> Rect {
|
fn collapse_view(&mut self) {
|
||||||
// Rect::new(
|
self.expanded = false;
|
||||||
// self.pos.x(),
|
self.dest = Rect::new(
|
||||||
// self.pos.y(),
|
self.dest.x(),
|
||||||
// self.icon_width,
|
self.dest.y(),
|
||||||
// self.icon_height,
|
self.icon_width() + self.name_width() + NAME_MARGIN as u32,
|
||||||
// )
|
self.icon_height(),
|
||||||
// }
|
);
|
||||||
|
|
||||||
pub fn source(&self) -> &Rect {
|
|
||||||
&self.source
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R) -> bool
|
pub fn open_directory<R>(&mut self, dir_path: String, renderer: &mut R) -> bool
|
||||||
@ -85,10 +274,12 @@ impl DirectoryView {
|
|||||||
_ if dir_path == self.path => {
|
_ if dir_path == self.path => {
|
||||||
if !self.opened {
|
if !self.opened {
|
||||||
self.opened = true;
|
self.opened = true;
|
||||||
self.expanded = true;
|
self.expand_view();
|
||||||
self.read_directory(renderer);
|
self.read_directory(renderer);
|
||||||
|
} else if self.expanded {
|
||||||
|
self.collapse_view();
|
||||||
} else {
|
} else {
|
||||||
self.expanded = !self.expanded;
|
self.expand_view();
|
||||||
}
|
}
|
||||||
self.calculate_size(renderer);
|
self.calculate_size(renderer);
|
||||||
true
|
true
|
||||||
@ -96,7 +287,7 @@ impl DirectoryView {
|
|||||||
_ if dir_path.contains((self.path.clone() + "/").as_str()) => {
|
_ if dir_path.contains((self.path.clone() + "/").as_str()) => {
|
||||||
if !self.opened {
|
if !self.opened {
|
||||||
self.opened = true;
|
self.opened = true;
|
||||||
self.expanded = true;
|
self.expand_view();
|
||||||
self.read_directory(renderer);
|
self.read_directory(renderer);
|
||||||
}
|
}
|
||||||
for dir in self.directories.iter_mut() {
|
for dir in self.directories.iter_mut() {
|
||||||
@ -124,18 +315,26 @@ impl DirectoryView {
|
|||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn name_width(&self) -> u32 {
|
pub fn name_width(&self) -> u32 {
|
||||||
self.name_width
|
self.name_label.name_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn icon_width(&self) -> u32 {
|
pub fn icon_width(&self) -> u32 {
|
||||||
self.icon_width
|
self.icon.width()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn icon_height(&self) -> u32 {
|
||||||
|
self.icon.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn height(&self) -> u32 {
|
pub fn height(&self) -> u32 {
|
||||||
match self.expanded {
|
match self.expanded {
|
||||||
true => self.height,
|
true => self.height,
|
||||||
false => self.icon_height,
|
false => self.icon.height(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,62 +381,6 @@ impl DirectoryView {
|
|||||||
self.directories.sort_by(|a, b| a.name().cmp(&b.name()));
|
self.directories.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_icon<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
|
||||||
where
|
|
||||||
C: CanvasAccess,
|
|
||||||
R: Renderer,
|
|
||||||
{
|
|
||||||
let dir_texture_path = {
|
|
||||||
let c = self.config.read().unwrap();
|
|
||||||
let mut themes_dir = c.directories().themes_dir.clone();
|
|
||||||
let path = c.theme().images().directory_icon();
|
|
||||||
themes_dir.push(path);
|
|
||||||
themes_dir.to_str().unwrap().to_owned()
|
|
||||||
};
|
|
||||||
if let Ok(texture) = renderer.load_image(dir_texture_path.clone()) {
|
|
||||||
canvas
|
|
||||||
.render_image(
|
|
||||||
texture,
|
|
||||||
self.source.clone(),
|
|
||||||
Rect::new(dest.x(), dest.y(), self.icon_width, self.icon_height),
|
|
||||||
)
|
|
||||||
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_name<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
|
||||||
where
|
|
||||||
C: CanvasAccess,
|
|
||||||
R: Renderer + CharacterSizeManager,
|
|
||||||
{
|
|
||||||
let mut d = dest.clone();
|
|
||||||
d.set_x(dest.x() + NAME_MARGIN);
|
|
||||||
let font_details = build_font_details(self);
|
|
||||||
let name = self.name();
|
|
||||||
let config = self.config.read().unwrap();
|
|
||||||
let text_color = config.theme().code_highlighting().title.color();
|
|
||||||
|
|
||||||
for c in name.chars() {
|
|
||||||
let size = renderer.load_character_size(c.clone());
|
|
||||||
let mut text_details = TextDetails {
|
|
||||||
color: Color::RGBA(text_color.r, text_color.g, text_color.b, text_color.a),
|
|
||||||
text: c.to_string(),
|
|
||||||
font: font_details.clone(),
|
|
||||||
};
|
|
||||||
let maybe_texture = renderer.load_text_tex(&mut text_details, font_details.clone());
|
|
||||||
|
|
||||||
if let Ok(texture) = maybe_texture {
|
|
||||||
d.set_width(size.width());
|
|
||||||
d.set_height(size.height());
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.render_image(texture, self.source.clone(), d.clone())
|
|
||||||
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
|
|
||||||
d.set_x(d.x() + size.width() as i32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_children<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
fn render_children<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
||||||
where
|
where
|
||||||
C: CanvasAccess,
|
C: CanvasAccess,
|
||||||
@ -248,8 +391,8 @@ impl DirectoryView {
|
|||||||
}
|
}
|
||||||
let mut point = dest.top_left()
|
let mut point = dest.top_left()
|
||||||
+ Point::new(
|
+ Point::new(
|
||||||
self.icon_width as i32 + CHILD_MARGIN,
|
self.icon_width() as i32 + CHILD_MARGIN,
|
||||||
self.icon_height as i32 + CHILD_MARGIN,
|
self.icon_height() as i32 + CHILD_MARGIN,
|
||||||
);
|
);
|
||||||
for dir in self.directories.iter() {
|
for dir in self.directories.iter() {
|
||||||
let context = RenderContext::ParentPosition(point.clone());
|
let context = RenderContext::ParentPosition(point.clone());
|
||||||
@ -269,14 +412,6 @@ impl DirectoryView {
|
|||||||
{
|
{
|
||||||
let size = renderer.load_character_size('W');
|
let size = renderer.load_character_size('W');
|
||||||
self.height = size.height();
|
self.height = size.height();
|
||||||
self.icon_height = size.height();
|
|
||||||
self.icon_width = size.height();
|
|
||||||
self.name_width = 0;
|
|
||||||
|
|
||||||
for c in self.name().chars() {
|
|
||||||
let size = renderer.load_character_size(c.clone());
|
|
||||||
self.name_width += size.width();
|
|
||||||
}
|
|
||||||
|
|
||||||
for dir in self.directories.iter_mut() {
|
for dir in self.directories.iter_mut() {
|
||||||
self.height = self.height + dir.height() + CHILD_MARGIN as u32;
|
self.height = self.height + dir.height() + CHILD_MARGIN as u32;
|
||||||
@ -288,144 +423,12 @@ impl DirectoryView {
|
|||||||
|
|
||||||
fn name_and_icon_rect(&self) -> Rect {
|
fn name_and_icon_rect(&self) -> Rect {
|
||||||
Rect::new(
|
Rect::new(
|
||||||
self.pos.x(),
|
self.dest.x(),
|
||||||
self.pos.y(),
|
self.dest.y(),
|
||||||
self.icon_width + self.name_width + NAME_MARGIN as u32,
|
self.icon.width() + self.name_width() + NAME_MARGIN as u32,
|
||||||
self.icon_height,
|
self.icon.height(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<R, C>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
|
||||||
where
|
|
||||||
R: Renderer + CharacterSizeManager,
|
|
||||||
C: CanvasAccess,
|
|
||||||
{
|
|
||||||
let dest = self.dest();
|
|
||||||
let move_point = match context {
|
|
||||||
&RenderContext::ParentPosition(p) => p.clone(),
|
|
||||||
_ => Point::new(0, 0),
|
|
||||||
};
|
|
||||||
let mut dest = move_render_point(move_point, &dest);
|
|
||||||
self.render_icon::<C, R>(canvas, renderer, &mut dest);
|
|
||||||
self.render_name::<C, R>(canvas, renderer, &mut dest.clone());
|
|
||||||
self.render_children::<C, R>(canvas, renderer, &mut dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
|
|
||||||
where
|
|
||||||
R: Renderer + CharacterSizeManager,
|
|
||||||
{
|
|
||||||
if self.opened {
|
|
||||||
for dir in self.directories.iter_mut() {
|
|
||||||
dir.prepare_ui(renderer);
|
|
||||||
}
|
|
||||||
for file in self.files.iter_mut() {
|
|
||||||
file.prepare_ui(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.calculate_size(renderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&mut self, ticks: i32, context: &UpdateContext) -> UpdateResult {
|
|
||||||
if !path::Path::new(&self.path).exists() {
|
|
||||||
return UpdateResult::RefreshFsTree;
|
|
||||||
}
|
|
||||||
if self.opened {
|
|
||||||
for dir in self.directories.iter_mut() {
|
|
||||||
dir.update(ticks, context);
|
|
||||||
}
|
|
||||||
for file in self.files.iter_mut() {
|
|
||||||
file.update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
UpdateResult::NoOp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_start_point(&self) -> Point {
|
|
||||||
self.pos.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn on_left_click(&mut self, point: &Point, context: &UpdateContext) -> UpdateResult {
|
|
||||||
let dest = self.dest();
|
|
||||||
let move_point = match context {
|
|
||||||
&UpdateContext::ParentPosition(p) => p.clone(),
|
|
||||||
_ => Point::new(0, 0),
|
|
||||||
};
|
|
||||||
let dest = move_render_point(move_point.clone(), &dest);
|
|
||||||
|
|
||||||
// icon or name is target of click
|
|
||||||
let icon_or_name = self.name_and_icon_rect();
|
|
||||||
if move_render_point(move_point, &icon_or_name).contains_point(point.clone()) {
|
|
||||||
return UpdateResult::OpenDirectory(self.path.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.expanded {
|
|
||||||
return UpdateResult::NoOp;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut p = dest.top_left()
|
|
||||||
+ Point::new(
|
|
||||||
self.icon_width as i32 + CHILD_MARGIN,
|
|
||||||
self.icon_height as i32 + CHILD_MARGIN,
|
|
||||||
);
|
|
||||||
for dir in self.directories.iter_mut() {
|
|
||||||
let context = UpdateContext::ParentPosition(p.clone());
|
|
||||||
if dir.is_left_click_target(&point, &context) {
|
|
||||||
return dir.on_left_click(&point, &context);
|
|
||||||
}
|
|
||||||
p = p + Point::new(0, dir.height() as i32 + CHILD_MARGIN);
|
|
||||||
}
|
|
||||||
for file in self.files.iter_mut() {
|
|
||||||
let context = UpdateContext::ParentPosition(p.clone());
|
|
||||||
if file.is_left_click_target(&point, &context) {
|
|
||||||
return file.on_left_click();
|
|
||||||
}
|
|
||||||
p = p + Point::new(0, file.height() as i32 + CHILD_MARGIN);
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateResult::NoOp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_left_click_target(&self, point: &Point, context: &UpdateContext) -> bool {
|
|
||||||
let dest = self.dest();
|
|
||||||
let move_point = match context {
|
|
||||||
UpdateContext::ParentPosition(p) => p.clone(),
|
|
||||||
_ => Point::new(0, 0),
|
|
||||||
};
|
|
||||||
let dest = move_render_point(move_point.clone(), &dest);
|
|
||||||
|
|
||||||
// icon or name is target of click
|
|
||||||
let name_and_icon_rect = self.name_and_icon_rect();
|
|
||||||
if move_render_point(move_point.clone(), &name_and_icon_rect).contains_point(point.clone())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if !self.expanded {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let mut p = dest.top_left()
|
|
||||||
+ Point::new(
|
|
||||||
self.icon_width as i32 + CHILD_MARGIN,
|
|
||||||
self.icon_height as i32 + CHILD_MARGIN,
|
|
||||||
);
|
|
||||||
// subdirectory is target of click
|
|
||||||
for dir in self.directories.iter() {
|
|
||||||
let context = UpdateContext::ParentPosition(p.clone());
|
|
||||||
if dir.is_left_click_target(&point, &context) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
p = p + Point::new(0, dir.height() as i32 + CHILD_MARGIN);
|
|
||||||
}
|
|
||||||
// file inside directory is target of click
|
|
||||||
for file in self.files.iter() {
|
|
||||||
let context = UpdateContext::ParentPosition(p.clone());
|
|
||||||
if file.is_left_click_target(&point, &context) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
p = p + Point::new(0, file.height() as i32 + CHILD_MARGIN);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigHolder for DirectoryView {
|
impl ConfigHolder for DirectoryView {
|
||||||
@ -569,7 +572,7 @@ mod tests {
|
|||||||
fn assert_initial_dest() {
|
fn assert_initial_dest() {
|
||||||
let config = build_config();
|
let config = build_config();
|
||||||
let widget = DirectoryView::new("/foo".to_owned(), config);
|
let widget = DirectoryView::new("/foo".to_owned(), config);
|
||||||
assert_eq!(widget.dest(), Rect::new(0, 0, 36, 16));
|
assert_eq!(widget.dest(), &Rect::new(0, 0, 36, 16));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -578,7 +581,7 @@ mod tests {
|
|||||||
let mut renderer = SimpleRendererMock::new(config.clone());
|
let mut renderer = SimpleRendererMock::new(config.clone());
|
||||||
let mut widget = DirectoryView::new("/foo".to_owned(), config);
|
let mut widget = DirectoryView::new("/foo".to_owned(), config);
|
||||||
widget.prepare_ui(&mut renderer);
|
widget.prepare_ui(&mut renderer);
|
||||||
assert_eq!(widget.dest(), Rect::new(0, 0, 73, 14));
|
assert_eq!(widget.dest(), &Rect::new(0, 0, 73, 14));
|
||||||
}
|
}
|
||||||
|
|
||||||
//##########################################################
|
//##########################################################
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
use crate::app::*;
|
use crate::app::*;
|
||||||
use crate::renderer::*;
|
use crate::renderer::*;
|
||||||
|
use crate::ui::icon::Icon;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use sdl2::pixels::Color;
|
|
||||||
use sdl2::rect::{Point, Rect};
|
use sdl2::rect::{Point, Rect};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
const ICON_DEST_WIDTH: u32 = 16;
|
const ICON_DEST_WIDTH: u32 = 16;
|
||||||
@ -12,168 +11,151 @@ const ICON_SRC_WIDTH: u32 = 64;
|
|||||||
const ICON_SRC_HEIGHT: u32 = 64;
|
const ICON_SRC_HEIGHT: u32 = 64;
|
||||||
|
|
||||||
pub struct FileEntry {
|
pub struct FileEntry {
|
||||||
name_width: u32,
|
|
||||||
icon_width: u32,
|
|
||||||
height: u32,
|
|
||||||
name: String,
|
|
||||||
path: String,
|
path: String,
|
||||||
dest: Rect,
|
inner: WidgetInner,
|
||||||
source: Rect,
|
icon: Icon,
|
||||||
config: ConfigAccess,
|
label: Label,
|
||||||
char_sizes: HashMap<char, Rect>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileEntry {
|
impl std::ops::Deref for FileEntry {
|
||||||
pub fn new(name: String, path: String, config: ConfigAccess) -> Self {
|
type Target = WidgetInner;
|
||||||
Self {
|
|
||||||
name,
|
fn deref(&self) -> &Self::Target {
|
||||||
path,
|
&self.inner
|
||||||
name_width: 0,
|
|
||||||
icon_width: 0,
|
|
||||||
height: 0,
|
|
||||||
dest: Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
|
|
||||||
source: Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
|
|
||||||
config,
|
|
||||||
char_sizes: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_width(&self) -> u32 {
|
impl std::ops::DerefMut for FileEntry {
|
||||||
self.name_width
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
}
|
&mut self.inner
|
||||||
|
|
||||||
pub fn icon_width(&self) -> u32 {
|
|
||||||
self.icon_width
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn height(&self) -> u32 {
|
|
||||||
self.height
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
|
||||||
self.name.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn path(&self) -> String {
|
|
||||||
self.path.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dest(&self) -> &Rect {
|
|
||||||
&self.dest
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn source(&self) -> &Rect {
|
|
||||||
&self.source
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn full_dest(&self) -> Rect {
|
|
||||||
Rect::new(
|
|
||||||
self.dest.x(),
|
|
||||||
self.dest.y(),
|
|
||||||
self.icon_width + NAME_MARGIN as u32 + self.name_width,
|
|
||||||
self.height,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_icon<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
|
||||||
where
|
|
||||||
C: CanvasAccess,
|
|
||||||
R: Renderer,
|
|
||||||
{
|
|
||||||
let dir_texture_path = {
|
|
||||||
let c = self.config.read().unwrap();
|
|
||||||
let mut themes_dir = c.directories().themes_dir.clone();
|
|
||||||
let path = c.theme().images().file_icon();
|
|
||||||
themes_dir.push(path);
|
|
||||||
themes_dir.to_str().unwrap().to_owned()
|
|
||||||
};
|
|
||||||
let maybe_tex = renderer.load_image(dir_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"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_name<C, R>(&self, canvas: &mut C, renderer: &mut R, dest: &mut Rect)
|
impl Widget for FileEntry {
|
||||||
where
|
fn texture_path(&self) -> Option<String> {
|
||||||
C: CanvasAccess,
|
None
|
||||||
R: Renderer,
|
|
||||||
{
|
|
||||||
let mut d = dest.clone();
|
|
||||||
d.set_x(dest.x() + NAME_MARGIN);
|
|
||||||
|
|
||||||
let font_details = build_font_details(self);
|
|
||||||
let name = self.name();
|
|
||||||
|
|
||||||
for c in name.chars() {
|
|
||||||
let size = self
|
|
||||||
.char_sizes
|
|
||||||
.get(&c)
|
|
||||||
.unwrap_or(&Rect::new(0, 0, 0, 0))
|
|
||||||
.clone();
|
|
||||||
let mut text_details = TextDetails {
|
|
||||||
color: Color::RGBA(255, 255, 255, 0),
|
|
||||||
text: c.to_string(),
|
|
||||||
font: font_details.clone(),
|
|
||||||
};
|
|
||||||
let maybe_texture = renderer.load_text_tex(&mut text_details, font_details.clone());
|
|
||||||
|
|
||||||
if let Ok(texture) = maybe_texture {
|
|
||||||
d.set_width(size.width());
|
|
||||||
d.set_height(size.height());
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.render_image(texture, self.source.clone(), d.clone())
|
|
||||||
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
|
|
||||||
d.set_x(d.x() + size.width() as i32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
fn dest(&self) -> &Rect {
|
||||||
where
|
&self.inner.dest
|
||||||
C: CanvasAccess,
|
|
||||||
R: Renderer,
|
|
||||||
{
|
|
||||||
let mut dest = match context {
|
|
||||||
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), &self.dest),
|
|
||||||
_ => self.dest.clone(),
|
|
||||||
};
|
|
||||||
self.render_icon(canvas, renderer, &mut dest);
|
|
||||||
self.render_name(canvas, renderer, &mut dest.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
|
fn set_dest(&mut self, rect: &Rect) {
|
||||||
where
|
self.inner.dest = rect.clone();
|
||||||
R: Renderer + CharacterSizeManager,
|
|
||||||
{
|
|
||||||
let w_rect = renderer.load_character_size('W');
|
|
||||||
self.char_sizes.insert('W', w_rect.clone());
|
|
||||||
self.height = w_rect.height();
|
|
||||||
self.icon_width = w_rect.height();
|
|
||||||
self.name_width = 0;
|
|
||||||
|
|
||||||
for c in self.name().chars() {
|
|
||||||
let size = { renderer.load_character_size(c.clone()) };
|
|
||||||
self.char_sizes.insert(c, size);
|
|
||||||
self.name_width += size.width();
|
|
||||||
}
|
|
||||||
self.dest.set_width(w_rect.height());
|
|
||||||
self.dest.set_height(w_rect.height());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self) -> UpdateResult {
|
fn source(&self) -> &Rect {
|
||||||
|
&self.inner.source
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_source(&mut self, rect: &Rect) {
|
||||||
|
self.inner.source = rect.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
|
||||||
if !path::Path::new(&self.path).exists() {
|
if !path::Path::new(&self.path).exists() {
|
||||||
return UpdateResult::RefreshFsTree;
|
return UpdateResult::RefreshFsTree;
|
||||||
}
|
}
|
||||||
UpdateResult::NoOp
|
UpdateResult::NoOp
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_start_point(&self) -> Point {
|
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
||||||
self.dest.top_left()
|
where
|
||||||
|
C: CanvasAccess,
|
||||||
|
R: Renderer + CharacterSizeManager,
|
||||||
|
{
|
||||||
|
let dest = match context {
|
||||||
|
&RenderContext::ParentPosition(p) => move_render_point(p.clone(), self.dest()),
|
||||||
|
_ => self.dest.clone(),
|
||||||
|
};
|
||||||
|
self.icon.render(
|
||||||
|
canvas,
|
||||||
|
renderer,
|
||||||
|
&RenderContext::ParentPosition(Point::new(dest.x(), dest.y())),
|
||||||
|
);
|
||||||
|
self.label.render(
|
||||||
|
canvas,
|
||||||
|
renderer,
|
||||||
|
&RenderContext::ParentPosition(Point::new(dest.x() + NAME_MARGIN, dest.y())),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_ui<R>(&mut self, renderer: &mut R)
|
||||||
|
where
|
||||||
|
R: Renderer + CharacterSizeManager,
|
||||||
|
{
|
||||||
|
let rect = renderer.load_character_size('W');
|
||||||
|
self.icon.prepare_ui(renderer);
|
||||||
|
self.icon.dest.set_height(rect.height());
|
||||||
|
self.icon.dest.set_width(rect.height());
|
||||||
|
self.label.prepare_ui(renderer);
|
||||||
|
let old = self.inner.dest.clone();
|
||||||
|
self.inner.dest = Rect::new(
|
||||||
|
old.x(),
|
||||||
|
old.y(),
|
||||||
|
self.name_width() + self.icon_width(),
|
||||||
|
self.height(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileEntry {
|
||||||
|
pub fn new(name: String, path: String, config: ConfigAccess) -> Self {
|
||||||
|
let texture_path = {
|
||||||
|
let c = config.read().unwrap();
|
||||||
|
let mut themes_dir = c.directories().themes_dir.clone();
|
||||||
|
let path = c.theme().images().file_icon();
|
||||||
|
themes_dir.push(path);
|
||||||
|
themes_dir.to_str().unwrap().to_owned()
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
inner: WidgetInner::new(
|
||||||
|
config.clone(),
|
||||||
|
Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
|
||||||
|
Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
|
||||||
|
),
|
||||||
|
icon: Icon::new(
|
||||||
|
config.clone(),
|
||||||
|
texture_path,
|
||||||
|
Rect::new(0, 0, ICON_SRC_WIDTH, ICON_SRC_HEIGHT),
|
||||||
|
Rect::new(0, 0, ICON_DEST_WIDTH, ICON_DEST_HEIGHT),
|
||||||
|
),
|
||||||
|
label: Label::new(name.clone(), config),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn name_width(&self) -> u32 {
|
||||||
|
self.label.name_width()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn icon_width(&self) -> u32 {
|
||||||
|
self.icon.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
self.dest().height()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.label.name()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn full_dest(&self) -> Rect {
|
||||||
|
Rect::new(
|
||||||
|
self.dest.x(),
|
||||||
|
self.dest.y(),
|
||||||
|
self.icon.width() + NAME_MARGIN as u32 + self.label.name_width(),
|
||||||
|
self.height(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn on_left_click(&mut self) -> UpdateResult {
|
pub fn on_left_click(&mut self) -> UpdateResult {
|
||||||
@ -184,7 +166,7 @@ impl FileEntry {
|
|||||||
let dest = Rect::new(
|
let dest = Rect::new(
|
||||||
self.dest.x(),
|
self.dest.x(),
|
||||||
self.dest.y(),
|
self.dest.y(),
|
||||||
self.icon_width + self.name_width + NAME_MARGIN as u32,
|
self.icon_width() + self.name_width() + NAME_MARGIN as u32,
|
||||||
self.dest.height(),
|
self.dest.height(),
|
||||||
);
|
);
|
||||||
let rect = match context {
|
let rect = match context {
|
||||||
@ -207,6 +189,7 @@ mod tests {
|
|||||||
use crate::tests::support::build_config;
|
use crate::tests::support::build_config;
|
||||||
use crate::tests::support::CanvasMock;
|
use crate::tests::support::CanvasMock;
|
||||||
use crate::tests::support::SimpleRendererMock;
|
use crate::tests::support::SimpleRendererMock;
|
||||||
|
use crate::ui::{UpdateContext, Widget};
|
||||||
|
|
||||||
//##########################################################
|
//##########################################################
|
||||||
// name_width
|
// name_width
|
||||||
@ -378,7 +361,10 @@ mod tests {
|
|||||||
let mut renderer = SimpleRendererMock::new(config.clone());
|
let mut renderer = SimpleRendererMock::new(config.clone());
|
||||||
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
|
let mut widget = FileEntry::new("bar.txt".to_owned(), "/foo".to_owned(), config);
|
||||||
widget.prepare_ui(&mut renderer);
|
widget.prepare_ui(&mut renderer);
|
||||||
assert_eq!(widget.update(), UpdateResult::RefreshFsTree);
|
assert_eq!(
|
||||||
|
widget.update(0, &UpdateContext::Nothing),
|
||||||
|
UpdateResult::RefreshFsTree
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -387,7 +373,10 @@ mod tests {
|
|||||||
let mut renderer = SimpleRendererMock::new(config.clone());
|
let mut renderer = SimpleRendererMock::new(config.clone());
|
||||||
let mut widget = FileEntry::new("bar.txt".to_owned(), "/tmp".to_owned(), config);
|
let mut widget = FileEntry::new("bar.txt".to_owned(), "/tmp".to_owned(), config);
|
||||||
widget.prepare_ui(&mut renderer);
|
widget.prepare_ui(&mut renderer);
|
||||||
assert_eq!(widget.update(), UpdateResult::NoOp);
|
assert_eq!(
|
||||||
|
widget.update(0, &UpdateContext::Nothing),
|
||||||
|
UpdateResult::NoOp
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
//##########################################################
|
//##########################################################
|
||||||
|
67
rider-editor/src/ui/icon.rs
Normal file
67
rider-editor/src/ui/icon.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use crate::ui::{Widget, WidgetInner};
|
||||||
|
use rider_config::ConfigAccess;
|
||||||
|
use sdl2::rect::Rect;
|
||||||
|
|
||||||
|
pub struct Icon {
|
||||||
|
path: String,
|
||||||
|
inner: WidgetInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Deref for Icon {
|
||||||
|
type Target = WidgetInner;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::DerefMut for Icon {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Icon {
|
||||||
|
pub fn new(config: ConfigAccess, path: String, source: Rect, dest: Rect) -> Self {
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
inner: WidgetInner::new(config, source, dest),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn height(&self) -> u32 {
|
||||||
|
self.inner.dest.height()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn width(&self) -> u32 {
|
||||||
|
self.inner.dest.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_texture_path(&mut self, path: String) {
|
||||||
|
self.path = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Icon {
|
||||||
|
fn texture_path(&self) -> Option<String> {
|
||||||
|
Some(self.path.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dest(&self) -> &Rect {
|
||||||
|
&self.inner.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dest(&mut self, rect: &Rect) {
|
||||||
|
self.inner.dest = rect.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> &Rect {
|
||||||
|
&self.inner.source
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_source(&mut self, rect: &Rect) {
|
||||||
|
self.inner.source = rect.clone();
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,7 @@ use crate::app::*;
|
|||||||
use crate::renderer::*;
|
use crate::renderer::*;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
use sdl2::rect::{Point, Rect};
|
use sdl2::rect::Rect;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const DEST_WIDTH: u32 = 16;
|
const DEST_WIDTH: u32 = 16;
|
||||||
@ -11,37 +11,47 @@ const SRC_WIDTH: u32 = 64;
|
|||||||
const SRC_HEIGHT: u32 = 64;
|
const SRC_HEIGHT: u32 = 64;
|
||||||
|
|
||||||
pub struct Label {
|
pub struct Label {
|
||||||
name_width: u32,
|
|
||||||
height: u32,
|
|
||||||
name: String,
|
name: String,
|
||||||
source: Rect,
|
|
||||||
dest: Rect,
|
|
||||||
char_sizes: HashMap<char, Rect>,
|
char_sizes: HashMap<char, Rect>,
|
||||||
config: ConfigAccess,
|
inner: WidgetInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Label {
|
impl std::ops::Deref for Label {
|
||||||
pub fn new(name: String, config: ConfigAccess) -> Self {
|
type Target = WidgetInner;
|
||||||
Self {
|
|
||||||
name,
|
fn deref(&self) -> &Self::Target {
|
||||||
name_width: 0,
|
&self.inner
|
||||||
height: 0,
|
|
||||||
dest: Rect::new(0, 0, DEST_WIDTH, DEST_HEIGHT),
|
|
||||||
source: Rect::new(0, 0, SRC_WIDTH, SRC_HEIGHT),
|
|
||||||
config,
|
|
||||||
char_sizes: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name_width(&self) -> u32 {
|
impl std::ops::DerefMut for Label {
|
||||||
self.name_width
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.inner
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
impl Widget for Label {
|
||||||
self.name.clone()
|
fn texture_path(&self) -> Option<String> {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
fn dest(&self) -> &Rect {
|
||||||
|
&self.inner.dest
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_dest(&mut self, rect: &Rect) {
|
||||||
|
self.inner.dest = rect.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&self) -> &Rect {
|
||||||
|
&self.inner.source
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_source(&mut self, rect: &Rect) {
|
||||||
|
self.inner.source = rect.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
||||||
where
|
where
|
||||||
C: CanvasAccess,
|
C: CanvasAccess,
|
||||||
R: Renderer,
|
R: Renderer,
|
||||||
@ -52,22 +62,25 @@ impl Label {
|
|||||||
};
|
};
|
||||||
let mut d = dest.clone();
|
let mut d = dest.clone();
|
||||||
d.set_x(dest.x() + NAME_MARGIN);
|
d.set_x(dest.x() + NAME_MARGIN);
|
||||||
|
canvas.set_clipping(d.clone());
|
||||||
|
|
||||||
let font_details = build_font_details(self);
|
let font_details = build_font_details(self);
|
||||||
for c in self.name.chars() {
|
for c in self.name.chars() {
|
||||||
let size = self
|
let size = self
|
||||||
.char_sizes
|
.char_sizes
|
||||||
.get(&c)
|
.get(&c)
|
||||||
.unwrap_or(&Rect::new(0, 0, 0, 0))
|
.cloned()
|
||||||
.clone();
|
.unwrap_or_else(|| Rect::new(0, 0, 0, 0));
|
||||||
let mut text_details = TextDetails {
|
renderer
|
||||||
|
.load_text_tex(
|
||||||
|
&mut TextDetails {
|
||||||
color: Color::RGBA(255, 255, 255, 0),
|
color: Color::RGBA(255, 255, 255, 0),
|
||||||
text: c.to_string(),
|
text: c.to_string(),
|
||||||
font: font_details.clone(),
|
font: font_details.clone(),
|
||||||
};
|
},
|
||||||
let maybe_texture = renderer.load_text_tex(&mut text_details, font_details.clone());
|
font_details.clone(),
|
||||||
|
)
|
||||||
if let Ok(texture) = maybe_texture {
|
.and_then(|texture| {
|
||||||
d.set_width(size.width());
|
d.set_width(size.width());
|
||||||
d.set_height(size.height());
|
d.set_height(size.height());
|
||||||
|
|
||||||
@ -75,42 +88,52 @@ impl Label {
|
|||||||
.render_image(texture, self.source.clone(), d.clone())
|
.render_image(texture, self.source.clone(), d.clone())
|
||||||
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
|
.unwrap_or_else(|_| panic!("Failed to draw directory entry texture"));
|
||||||
d.set_x(d.x() + size.width() as i32);
|
d.set_x(d.x() + size.width() as i32);
|
||||||
}
|
Ok(())
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
eprintln!("Failed to render label \"{:?}\": {:?}", self.name(), e)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn prepare_ui<R>(&mut self, renderer: &mut R)
|
fn prepare_ui<R>(&mut self, renderer: &mut R)
|
||||||
where
|
where
|
||||||
R: Renderer + CharacterSizeManager,
|
R: Renderer + CharacterSizeManager,
|
||||||
{
|
{
|
||||||
let w_rect = renderer.load_character_size('W');
|
let w_rect = renderer.load_character_size('W');
|
||||||
self.char_sizes.insert('W', w_rect.clone());
|
self.char_sizes.insert('W', w_rect.clone());
|
||||||
self.height = w_rect.height();
|
let mut name_width = 0;
|
||||||
self.name_width = 0;
|
|
||||||
|
|
||||||
for c in self.name().chars() {
|
for c in self.name().chars() {
|
||||||
let size = { renderer.load_character_size(c.clone()) };
|
let size = { renderer.load_character_size(c.clone()) };
|
||||||
self.char_sizes.insert(c, size);
|
self.char_sizes.insert(c, size);
|
||||||
self.name_width += size.width();
|
name_width += size.width();
|
||||||
}
|
}
|
||||||
self.dest.set_width(w_rect.height());
|
self.dest.set_width(name_width);
|
||||||
self.dest.set_height(w_rect.height());
|
self.dest.set_height(w_rect.height());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RenderBox for Label {
|
impl Label {
|
||||||
fn render_start_point(&self) -> Point {
|
pub fn new(name: String, config: ConfigAccess) -> Self {
|
||||||
self.dest.top_left()
|
Self {
|
||||||
}
|
name,
|
||||||
|
char_sizes: HashMap::new(),
|
||||||
fn dest(&self) -> Rect {
|
inner: WidgetInner::new(
|
||||||
self.dest.clone()
|
config,
|
||||||
|
Rect::new(0, 0, SRC_WIDTH, SRC_HEIGHT),
|
||||||
|
Rect::new(0, 0, DEST_WIDTH, DEST_HEIGHT),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Update for Label {
|
#[inline]
|
||||||
fn update(&mut self, _ticks: i32, _context: &UpdateContext) -> UpdateResult {
|
pub fn name_width(&self) -> u32 {
|
||||||
UpdateResult::NoOp
|
self.dest.width()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> String {
|
||||||
|
self.name.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ impl MenuBar {
|
|||||||
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
pub fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
||||||
where
|
where
|
||||||
C: CanvasAccess,
|
C: CanvasAccess,
|
||||||
R: Renderer,
|
R: Renderer + CharacterSizeManager,
|
||||||
{
|
{
|
||||||
use std::borrow::*;
|
use std::borrow::*;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ pub mod caret;
|
|||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod file_editor;
|
pub mod file_editor;
|
||||||
pub mod filesystem;
|
pub mod filesystem;
|
||||||
|
pub mod icon;
|
||||||
pub mod label;
|
pub mod label;
|
||||||
pub mod menu_bar;
|
pub mod menu_bar;
|
||||||
pub mod modal;
|
pub mod modal;
|
||||||
@ -160,35 +161,6 @@ impl WidgetInner {
|
|||||||
pub trait Widget {
|
pub trait Widget {
|
||||||
fn texture_path(&self) -> Option<String>;
|
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 dest(&self) -> &Rect;
|
||||||
|
|
||||||
fn set_dest(&mut self, rect: &Rect);
|
fn set_dest(&mut self, rect: &Rect);
|
||||||
@ -233,6 +205,35 @@ pub trait Widget {
|
|||||||
fn padding_height(&self) -> u32 {
|
fn padding_height(&self) -> u32 {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render<C, R>(&self, canvas: &mut C, renderer: &mut R, context: &RenderContext)
|
||||||
|
where
|
||||||
|
C: CanvasAccess,
|
||||||
|
R: Renderer + CharacterSizeManager,
|
||||||
|
{
|
||||||
|
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: Renderer + CharacterSizeManager,
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -4,11 +4,11 @@ use crate::ui::filesystem::directory::DirectoryView;
|
|||||||
use crate::ui::horizontal_scroll_bar::HorizontalScrollBar;
|
use crate::ui::horizontal_scroll_bar::HorizontalScrollBar;
|
||||||
use crate::ui::text_character::CharacterSizeManager;
|
use crate::ui::text_character::CharacterSizeManager;
|
||||||
use crate::ui::vertical_scroll_bar::VerticalScrollBar;
|
use crate::ui::vertical_scroll_bar::VerticalScrollBar;
|
||||||
use crate::ui::CanvasAccess;
|
|
||||||
use crate::ui::ClickHandler;
|
use crate::ui::ClickHandler;
|
||||||
use crate::ui::RenderContext;
|
use crate::ui::RenderContext;
|
||||||
use crate::ui::UpdateContext;
|
use crate::ui::UpdateContext;
|
||||||
use crate::ui::{move_render_point, ScrollView};
|
use crate::ui::{move_render_point, ScrollView};
|
||||||
|
use crate::ui::{CanvasAccess, Widget};
|
||||||
use rider_config::config::Config;
|
use rider_config::config::Config;
|
||||||
use rider_config::ConfigHolder;
|
use rider_config::ConfigHolder;
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
|
Loading…
Reference in New Issue
Block a user