sync commit
This commit is contained in:
parent
3388ae4096
commit
ba7703e4c2
13
Cargo.toml
13
Cargo.toml
|
@ -23,7 +23,20 @@ path = "examples/component_params.rs"
|
|||
name = "keyboard"
|
||||
path = "examples/keyboard.rs"
|
||||
|
||||
[[example]]
|
||||
name = "navigation"
|
||||
path = "examples/navigation.rs"
|
||||
|
||||
[[example]]
|
||||
name = "stack"
|
||||
path = "examples/stack.rs"
|
||||
|
||||
[[example]]
|
||||
name = "paragraph"
|
||||
path = "examples/paragraph.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.71"
|
||||
crossterm = "0.26.1"
|
||||
ctrlc = "3.3.1"
|
||||
textwrap = "0.16.0"
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
use arkham::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Route {
|
||||
Main,
|
||||
Secondary,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = App::new(root).insert_state(Route::Main).run();
|
||||
}
|
||||
|
||||
fn root(ctx: &mut ViewContext, route: State<Route>) {
|
||||
let size = ctx.size();
|
||||
let r = *route.get();
|
||||
ctx.insert((0, 0), "Press Q to quit");
|
||||
match r {
|
||||
Route::Main => {
|
||||
ctx.component(Rect::new((0, 1), size), main_route);
|
||||
}
|
||||
Route::Secondary => {
|
||||
ctx.component(Rect::new((0, 1), size), secondary_route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main_route(ctx: &mut ViewContext, kb: Res<Keyboard>, route: State<Route>) {
|
||||
ctx.insert(
|
||||
0,
|
||||
"Welcome to the main screen, press 2 to goto the secondary screen",
|
||||
);
|
||||
if kb.char() == Some('2') {
|
||||
*route.get_mut() = Route::Secondary;
|
||||
ctx.render();
|
||||
}
|
||||
}
|
||||
|
||||
fn secondary_route(ctx: &mut ViewContext, kb: Res<Keyboard>, route: State<Route>) {
|
||||
ctx.insert(
|
||||
0,
|
||||
"Welcome to the secondary screen, press 1 to goto the main screen",
|
||||
);
|
||||
|
||||
if kb.char() == Some('1') {
|
||||
*route.get_mut() = Route::Main;
|
||||
ctx.render();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
use arkham::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let _ = App::new(root).run();
|
||||
}
|
||||
|
||||
fn root(ctx: &mut ViewContext) {
|
||||
let mut stack = ctx.vertical_stack(ctx.size());
|
||||
let p = arkham::components::Paragraph::new("Rust is a multi-paradigm, general-purpose programming language that emphasizes performance, type safety, and concurrency. It enforces memory safety—ensuring that all references point to valid memory—without requiring the use of a garbage collector or reference counting present in other memory-safe languages.");
|
||||
stack.component(Size::new(100_usize, p.height(100)), p);
|
||||
let p = arkham::components::Paragraph::new("Rust is a multi-paradigm, general-purpose programming language that emphasizes performance, type safety, and concurrency. It enforces memory safety—ensuring that all references point to valid memory—without requiring the use of a garbage collector or reference counting present in other memory-safe languages.");
|
||||
stack.component(Size::new(100_usize, p.height(100)), p);
|
||||
ctx.component(ctx.size(), stack);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
use arkham::prelude::*;
|
||||
|
||||
fn main() {
|
||||
let _ = App::new(root).run();
|
||||
}
|
||||
|
||||
fn root(ctx: &mut ViewContext) {
|
||||
let mut stack = ctx.vertical_stack(Size::new(100, 100));
|
||||
for _ in 0..10 {
|
||||
stack.component(Size::new(ctx.size().width, 2), list_item);
|
||||
}
|
||||
ctx.component(Rect::new(0, (100, 100)), stack);
|
||||
}
|
||||
|
||||
fn list_item(ctx: &mut ViewContext) {
|
||||
ctx.insert(0, "line 1");
|
||||
}
|
24
src/app.rs
24
src/app.rs
|
@ -43,15 +43,10 @@ where
|
|||
container,
|
||||
root,
|
||||
main_view,
|
||||
args: PhantomData::default(),
|
||||
args: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to change the root function that is used during each render cycle.
|
||||
pub fn change_root(&mut self, root: F) {
|
||||
self.root = root;
|
||||
}
|
||||
|
||||
/// Insert a resource which can be injected into component functions.
|
||||
///
|
||||
/// This resource can only be accessed immutably by reference.
|
||||
|
@ -131,12 +126,19 @@ where
|
|||
terminal::enable_raw_mode()?;
|
||||
|
||||
loop {
|
||||
let mut context =
|
||||
ViewContext::new(self.container.clone(), terminal::size().unwrap().into());
|
||||
loop {
|
||||
let mut context =
|
||||
ViewContext::new(self.container.clone(), terminal::size().unwrap().into());
|
||||
|
||||
self.container.borrow().call(&mut context, &self.root);
|
||||
self.main_view.apply((0, 0), context.view);
|
||||
self.render()?;
|
||||
self.root
|
||||
.call(&mut context, Args::from_container(&self.container.borrow()));
|
||||
self.main_view.apply((0, 0), &context.view);
|
||||
self.render()?;
|
||||
|
||||
if !context.rerender {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.container
|
||||
.borrow()
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
mod paragraph;
|
||||
pub use paragraph::Paragraph;
|
|
@ -0,0 +1,38 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use crossterm::style::Color;
|
||||
|
||||
use crate::prelude::{Callable, ToRuneExt};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Paragraph {
|
||||
content: String,
|
||||
fg: Option<Color>,
|
||||
bg: Option<Color>,
|
||||
}
|
||||
|
||||
impl Paragraph {
|
||||
pub fn new(content: &str) -> Self {
|
||||
Self {
|
||||
content: content.to_string(),
|
||||
fg: None,
|
||||
bg: None,
|
||||
}
|
||||
}
|
||||
pub fn height(&self, width: usize) -> usize {
|
||||
textwrap::wrap(&self.content, width).len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Callable<()> for Paragraph {
|
||||
fn call(&self, view: &mut crate::prelude::ViewContext, _args: ()) {
|
||||
let lines = textwrap::wrap(&self.content, view.width());
|
||||
let mut stack = view.vertical_stack(view.size());
|
||||
for line in lines.iter() {
|
||||
let l = line.deref().to_runes();
|
||||
|
||||
stack.insert(line);
|
||||
}
|
||||
view.component(view.size(), stack);
|
||||
}
|
||||
}
|
|
@ -7,14 +7,10 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::context::ViewContext;
|
||||
type ArkhamResult = anyhow::Result<ArkhamState>;
|
||||
pub enum ArkhamState {
|
||||
Noop,
|
||||
}
|
||||
|
||||
/// The container stores typed resource and state objects and provides
|
||||
/// them to component functions.
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct Container {
|
||||
bindings: HashMap<TypeId, Box<dyn Any>>,
|
||||
}
|
||||
|
@ -36,15 +32,6 @@ impl Container {
|
|||
.get(&TypeId::of::<T>())
|
||||
.and_then(|boxed| boxed.downcast_ref())
|
||||
}
|
||||
|
||||
/// Call a function while providing dependency injcetion.
|
||||
pub fn call<F, Args>(&self, view: &mut ViewContext, callable: &F)
|
||||
where
|
||||
F: Callable<Args>,
|
||||
Args: FromContainer,
|
||||
{
|
||||
let _ = callable.call(view, Args::from_container(self));
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for state objcets. This internally holds a reference counted
|
||||
|
@ -136,24 +123,23 @@ impl<T: ?Sized + 'static> FromContainer for Res<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Callable must be implemented for functions that can be used as component
|
||||
/// functions. They are given a ViewContext for the component function and
|
||||
/// injectable arguments.
|
||||
pub trait Callable<Args> {
|
||||
fn call(&self, view: &mut ViewContext, args: Args);
|
||||
}
|
||||
|
||||
impl<Func> Callable<()> for Func
|
||||
where
|
||||
Func: Fn(&mut ViewContext),
|
||||
{
|
||||
#[inline]
|
||||
fn call(&self, view: &mut ViewContext, _args: ()) -> ArkhamResult {
|
||||
fn call(&self, view: &mut ViewContext, _args: ()) {
|
||||
(self)(view);
|
||||
Ok(ArkhamState::Noop)
|
||||
}
|
||||
}
|
||||
|
||||
/// Callable must be implemented for functions that can be used as component
|
||||
/// functions. They are given a ViewContext for the component function and
|
||||
/// injectable arguments.
|
||||
pub trait Callable<Args> {
|
||||
fn call(&self, view: &mut ViewContext, args: Args) -> ArkhamResult;
|
||||
}
|
||||
|
||||
/// FromContainer must be implmented for objects that can be injected into
|
||||
/// component functions. This includes the Res and State structs.
|
||||
pub trait FromContainer {
|
||||
|
@ -172,9 +158,8 @@ macro_rules! callable_tuple ({ $($param:ident)* } => {
|
|||
{
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn call(&self, view: &mut ViewContext , ($($param,)*): ($($param,)*)) -> ArkhamResult{
|
||||
fn call(&self, view: &mut ViewContext , ($($param,)*): ($($param,)*)) {
|
||||
(self)(view, $($param,)*);
|
||||
Ok(ArkhamState::Noop)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -217,18 +202,3 @@ tuple_from_tm! { A B C D E F G H I }
|
|||
tuple_from_tm! { A B C D E F G H I J }
|
||||
tuple_from_tm! { A B C D E F G H I J K }
|
||||
tuple_from_tm! { A B C D E F G H I J K L }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{container::Container, prelude::ViewContext};
|
||||
|
||||
#[test]
|
||||
fn test_no_params() {
|
||||
fn test_f(_ctx: &mut ViewContext) {}
|
||||
let container = Rc::new(RefCell::new(Container::default()));
|
||||
let mut ctx = ViewContext::new(container.clone(), (1, 1).into());
|
||||
container.borrow().call(&mut ctx, &test_f);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::container::{Callable, FromContainer};
|
||||
use crate::{
|
||||
container::{Callable, FromContainer},
|
||||
stack::Stack,
|
||||
};
|
||||
|
||||
use super::{
|
||||
container::Container,
|
||||
|
@ -15,6 +18,7 @@ use super::{
|
|||
pub struct ViewContext {
|
||||
pub view: View,
|
||||
pub container: Rc<RefCell<Container>>,
|
||||
pub(crate) rerender: bool,
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ViewContext {
|
||||
|
@ -38,7 +42,35 @@ impl ViewContext {
|
|||
pub fn new(container: Rc<RefCell<Container>>, size: Size) -> Self {
|
||||
let view = View::new(size);
|
||||
|
||||
Self { view, container }
|
||||
Self {
|
||||
view,
|
||||
container,
|
||||
rerender: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify the application to rerender the view. This is useful after a
|
||||
/// state change that might affect other views.
|
||||
pub fn render(&mut self) {
|
||||
self.rerender = true;
|
||||
}
|
||||
|
||||
pub fn vertical_stack(&self, size: Size) -> Stack {
|
||||
Stack {
|
||||
direction: crate::stack::StackDirection::Vertical,
|
||||
container: self.container.clone(),
|
||||
view: View::new(size),
|
||||
position: Pos::from(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn horizontal_stack(&self, size: Size) -> Stack {
|
||||
Stack {
|
||||
direction: crate::stack::StackDirection::Horizontal,
|
||||
container: self.container.clone(),
|
||||
view: View::new(size),
|
||||
position: Pos::from(0),
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute a component function. The passed function will receive a new
|
||||
|
@ -53,8 +85,10 @@ impl ViewContext {
|
|||
{
|
||||
let rect = rect.into();
|
||||
let mut context = ViewContext::new(self.container.clone(), rect.size);
|
||||
self.container.borrow().call(&mut context, &f);
|
||||
self.view.apply(rect.pos, context.view);
|
||||
let args = Args::from_container(&self.container.borrow());
|
||||
f.call(&mut context, args);
|
||||
self.view.apply(rect.pos, &context.view);
|
||||
self.rerender = context.rerender;
|
||||
}
|
||||
|
||||
/// Set a specific rune to a specific position. This function can be used
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::ops::{Add, Sub};
|
||||
use std::ops::{Add, AddAssign, Sub};
|
||||
|
||||
/// Pos represents a coordinate position within the termianl screen.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -7,6 +7,12 @@ pub struct Pos {
|
|||
pub y: usize,
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
pub fn new(x: usize, y: usize) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize)> for Pos {
|
||||
fn from(value: (usize, usize)) -> Self {
|
||||
Self {
|
||||
|
@ -22,6 +28,23 @@ impl From<usize> for Pos {
|
|||
}
|
||||
}
|
||||
|
||||
impl Add<Pos> for Pos {
|
||||
type Output = Pos;
|
||||
|
||||
fn add(mut self, rhs: Pos) -> Self::Output {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl AddAssign<Pos> for Pos {
|
||||
fn add_assign(&mut self, rhs: Pos) {
|
||||
self.x += rhs.x;
|
||||
self.y += rhs.y;
|
||||
}
|
||||
}
|
||||
|
||||
// An area that can be operated on.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Size {
|
||||
|
@ -29,6 +52,12 @@ pub struct Size {
|
|||
pub height: usize,
|
||||
}
|
||||
|
||||
impl Size {
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<Size> for Size {
|
||||
type Output = Size;
|
||||
|
||||
|
@ -100,6 +129,15 @@ impl From<(i32, i32)> for Size {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Size {
|
||||
fn from(value: i32) -> Self {
|
||||
Self {
|
||||
width: value as usize,
|
||||
height: value as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An area of the screen with a given size and postiion. The position
|
||||
/// represents the top-left corner of the rectangle.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
@ -120,6 +158,10 @@ impl Rect {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn zero() -> Self {
|
||||
Self::new(0, 0)
|
||||
}
|
||||
|
||||
pub fn with_size<S>(size: S) -> Self
|
||||
where
|
||||
S: Into<Size>,
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
mod app;
|
||||
pub mod components;
|
||||
mod container;
|
||||
mod context;
|
||||
mod geometry;
|
||||
mod input;
|
||||
mod plugins;
|
||||
mod runes;
|
||||
mod stack;
|
||||
pub mod symbols;
|
||||
mod theme;
|
||||
mod view;
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
pub struct KeybindPlugin;
|
|
@ -0,0 +1,13 @@
|
|||
use crate::prelude::ViewContext;
|
||||
|
||||
pub mod keybind;
|
||||
|
||||
pub trait Pluigin {
|
||||
fn build(ctx: ViewContext);
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn before_render(ctx: ViewContext) {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn after_render(ctx: ViewContext) {}
|
||||
}
|
51
src/runes.rs
51
src/runes.rs
|
@ -1,6 +1,8 @@
|
|||
use crossterm::{
|
||||
queue,
|
||||
style::{Attribute, Color, Print, SetAttribute, SetBackgroundColor, SetForegroundColor},
|
||||
style::{
|
||||
Attribute, Color, Print, ResetColor, SetAttribute, SetBackgroundColor, SetForegroundColor,
|
||||
},
|
||||
};
|
||||
|
||||
/// Rune repesents the state of the screen at a specific position. It stores
|
||||
|
@ -20,6 +22,25 @@ impl std::fmt::Debug for Rune {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::Add<Rune> for Rune {
|
||||
type Output = Rune;
|
||||
|
||||
fn add(self, mut rhs: Rune) -> Self::Output {
|
||||
rhs.fg = rhs.fg.or(self.fg);
|
||||
rhs.bg = rhs.bg.or(self.bg);
|
||||
rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl From<char> for Rune {
|
||||
fn from(value: char) -> Self {
|
||||
Rune {
|
||||
content: Some(value),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Rune {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
|
@ -48,6 +69,7 @@ impl Rune {
|
|||
W: std::io::Write,
|
||||
{
|
||||
if let Some(content) = self.content {
|
||||
queue!(out, ResetColor)?;
|
||||
if let Some(c) = self.fg {
|
||||
queue!(out, SetForegroundColor(c))?;
|
||||
}
|
||||
|
@ -65,7 +87,7 @@ impl Rune {
|
|||
|
||||
/// Runes represetns a series of runes. This is generally used to convert
|
||||
/// strings into Runes and apply styling information to them.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Runes(Vec<Rune>);
|
||||
|
||||
impl std::ops::Deref for Runes {
|
||||
|
@ -76,6 +98,12 @@ impl std::ops::Deref for Runes {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Rune> for Runes {
|
||||
fn from(value: Rune) -> Self {
|
||||
Runes::new(vec![value])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToString> From<T> for Runes {
|
||||
fn from(value: T) -> Self {
|
||||
Runes(
|
||||
|
@ -89,12 +117,20 @@ impl<T: ToString> From<T> for Runes {
|
|||
}
|
||||
|
||||
impl Runes {
|
||||
pub fn fg(mut self, color: Color) -> Self {
|
||||
pub fn new(runes: Vec<Rune>) -> Self {
|
||||
Self(runes)
|
||||
}
|
||||
pub fn fg(self, color: Color) -> Self {
|
||||
self.set_fg(Some(color))
|
||||
}
|
||||
|
||||
pub fn set_fg(mut self, color: Option<Color>) -> Self {
|
||||
for r in self.0.iter_mut() {
|
||||
r.fg = Some(color);
|
||||
r.fg = color;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn bg(mut self, color: Color) -> Self {
|
||||
for r in self.0.iter_mut() {
|
||||
r.bg = Some(color);
|
||||
|
@ -107,6 +143,13 @@ impl Runes {
|
|||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn add<R>(&mut self, runes: R)
|
||||
where
|
||||
R: Into<Runes>,
|
||||
{
|
||||
self.0.append(&mut runes.into().0);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToRuneExt {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{
|
||||
container::Container,
|
||||
prelude::{Callable, Pos, Runes, Size, ViewContext},
|
||||
view::View,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum StackDirection {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Stack {
|
||||
pub(crate) direction: StackDirection,
|
||||
pub(crate) container: Rc<RefCell<Container>>,
|
||||
pub(crate) view: View,
|
||||
pub(crate) position: Pos,
|
||||
}
|
||||
|
||||
impl Stack {
|
||||
pub fn component<F, Args, S>(&mut self, size: S, f: F)
|
||||
where
|
||||
F: crate::prelude::Callable<Args>,
|
||||
Args: crate::prelude::FromContainer,
|
||||
S: Into<Size>,
|
||||
{
|
||||
let size = size.into();
|
||||
let mut context = ViewContext::new(self.container.clone(), size);
|
||||
f.call(&mut context, Args::from_container(&self.container.borrow()));
|
||||
self.view.apply(self.position, &context.view);
|
||||
self.position += match self.direction {
|
||||
StackDirection::Vertical => Pos::new(0, size.height),
|
||||
StackDirection::Horizontal => Pos::new(size.width, 0),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn insert<R: Into<Runes>>(&mut self, value: R) {
|
||||
let runes: Runes = value.into();
|
||||
let l = runes.len();
|
||||
self.view.insert(self.position, runes);
|
||||
self.position += match self.direction {
|
||||
StackDirection::Vertical => Pos::new(0, 1),
|
||||
StackDirection::Horizontal => Pos::new(l, 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl Callable<()> for Stack {
|
||||
fn call(&self, ctx: &mut ViewContext, _args: ()) {
|
||||
ctx.apply((0, 0), &self.view);
|
||||
}
|
||||
}
|
14
src/view.rs
14
src/view.rs
|
@ -8,7 +8,7 @@ use crate::{
|
|||
/// A renderable region. View stores the renderable state of an area of the
|
||||
/// screen. Views can be combined together to achieve a finalized view that
|
||||
/// repsresents the entire screens next render.
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct View(pub Vec<Vec<Rune>>);
|
||||
|
||||
impl std::ops::DerefMut for View {
|
||||
|
@ -41,13 +41,14 @@ impl View {
|
|||
}
|
||||
|
||||
/// Apply another view onto this view at a given position.
|
||||
pub fn apply<P: Into<Pos>>(&mut self, pos: P, view: View) {
|
||||
pub fn apply<P: Into<Pos>>(&mut self, pos: P, view: &View) {
|
||||
let pos = pos.into();
|
||||
for (y, line) in view.0.iter().enumerate() {
|
||||
if self.0.len() > y + pos.y {
|
||||
for (x, rune) in line.iter().enumerate() {
|
||||
if rune.content.is_some() && self.0[y].len() > x + pos.x {
|
||||
let _ = std::mem::replace(&mut self.0[y + pos.y][x + pos.x], *rune);
|
||||
let rune = (self.0[y + pos.y][x + pos.x]) + *rune;
|
||||
let _ = std::mem::replace(&mut self.0[y + pos.y][x + pos.x], rune);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +109,8 @@ impl View {
|
|||
.take((line_len - x as i32).max(0) as usize)
|
||||
.enumerate()
|
||||
{
|
||||
let _ = std::mem::replace(&mut line[x + i], *c);
|
||||
let rune = line[x + i] + *c;
|
||||
let _ = std::mem::replace(&mut line[x + i], rune);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +174,7 @@ mod tests {
|
|||
let mut view1 = View::new((3, 4));
|
||||
view1.fill(Rect::new((1, 1), (2, 2)), Rune::new().content('X'));
|
||||
let mut view2 = View::new((3, 4));
|
||||
view2.apply((0, 1), view1);
|
||||
view2.apply((0, 1), &view1);
|
||||
dbg!(&view2.0);
|
||||
assert_eq!(view2.0[0][0].content, None);
|
||||
assert_eq!(view2.0[0][1].content, None);
|
||||
|
@ -196,7 +198,7 @@ mod tests {
|
|||
let mut view0 = View::new((5, 5));
|
||||
view0.fill(Rect::new((1, 1), (4, 4)), Rune::new().content('X'));
|
||||
let mut view = View::new((3, 3));
|
||||
view.apply((0, 0), view0);
|
||||
view.apply((0, 0), &view0);
|
||||
dbg!(&view.0);
|
||||
assert_eq!(view.0[0][0].content, None);
|
||||
assert_eq!(view.0[0][1].content, None);
|
||||
|
|
Loading…
Reference in New Issue