Moving Windows in StackView (#200)

* Inital draft of reposition code.

* throwing it all in git so I can show @gycos the mess I made

* Cleaning up the example and removing that garbage getter function from StackView. More thought needs to be applied to that API.

* modified stackview to locally draw background, currently every frame. Adjusted surrounding elements to mirror this change.

* fixed dirty flag on stackView to use a cell. Also added dirty events on window resize.

* Final code cleanup.

* Fixed all highlighted issues in PR #200
This commit is contained in:
SEGFAULT 2018-02-08 01:25:00 +01:00 committed by Alexandre Bury
parent 22e2360aa1
commit 9e591ef635
3 changed files with 151 additions and 24 deletions

56
examples/position.rs Normal file
View File

@ -0,0 +1,56 @@
extern crate cursive;
use cursive::Cursive;
use cursive::view::Position;
use cursive::views::LayerPosition;
use cursive::views::TextView;
/// Moves top layer by the specified amount
fn move_top(c: &mut Cursive, x_in: isize, y_in: isize) {
// Step 1. Get the current position of the layer.
let s = c.screen_mut();
let l = LayerPosition::FromFront(0);
let (x, y) = s.offset().pair();
// Step 2. add the specifed amount
// (unsigned math in Rust is a mess.)
let x = if x_in < 0 {
x - (-x_in) as usize
} else {
x + x_in as usize
};
let y = if y_in < 0 {
y - (-y_in) as usize
} else {
y + y_in as usize
};
// convert the new x and y into a position
let p = Position::absolute((x, y));
// Step 3. Apply the new position
s.reposition_layer(l, p);
}
fn main() {
let mut siv = Cursive::new();
siv.set_fps(60);
// We can quit by pressing `q`
siv.add_global_callback('q', Cursive::quit);
// Next Gen FPS Controls.
siv.add_global_callback('w', |s| move_top(s, 0, -1));
siv.add_global_callback('a', |s| move_top(s, -1, 0));
siv.add_global_callback('s', |s| move_top(s, 0, 1));
siv.add_global_callback('d', |s| move_top(s, 1, 0));
// Add window to fly around.
siv.add_layer(TextView::new(
"Press w,a,s,d to move the window.\n\
Press q to quit the application.",
));
// Run the event loop
siv.run();
}

View File

@ -9,8 +9,8 @@ use std::path::Path;
use std::sync::mpsc;
use theme;
use vec::Vec2;
use view::{self, AnyView, Finder, View};
use views;
use view::{self, AnyView, Finder, Position, View};
use views::{self, LayerPosition};
/// Identifies a screen in the cursive root.
pub type ScreenId = usize;
@ -416,9 +416,14 @@ impl Cursive {
/// Convenient method to remove a layer from the current screen.
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
let result = self.screen_mut().pop_layer();
self.clear();
result
self.screen_mut().pop_layer()
}
/// Convenient stub forwarding layer repositioning.
pub fn reposition_layer(
&mut self, layer: LayerPosition, position: Position
) {
self.screen_mut().reposition_layer(layer, position);
}
// Handles a key event when it was ignored by the current view
@ -460,6 +465,15 @@ impl Cursive {
let printer =
Printer::new(self.screen_size(), &self.theme, &self.backend);
let selected = self.menubar.receive_events();
// Print the stackview background before the menubar
let offset = if self.menubar.autohide { 0 } else { 1 };
let id = self.active_screen;
let sv_printer = printer.offset((0, offset), !selected);
self.screens[id].draw_bg(&sv_printer);
// Draw the currently active screen
// If the menubar is active, nothing else can be.
// Draw the menubar?
@ -472,12 +486,9 @@ impl Cursive {
self.menubar.draw(&printer);
}
let selected = self.menubar.receive_events();
let offset = if self.menubar.autohide { 0 } else { 1 };
let printer = printer.offset((0, offset), !selected);
let id = self.active_screen;
self.screens[id].draw(&printer);
// finally draw stackview layers
// using variables from above
self.screens[id].draw_fg(&sv_printer);
}
/// Returns `true` until [`quit(&mut self)`] is called.

View File

@ -3,6 +3,7 @@ use With;
use direction::Direction;
use event::{Event, EventResult};
use std::any::Any;
use std::cell;
use std::ops::Deref;
use theme::ColorStyle;
use vec::Vec2;
@ -15,6 +16,9 @@ pub struct StackView {
// Store layers from back to front.
layers: Vec<Child>,
last_size: Vec2,
// Flag indicates if undrawn areas of the background are exposed
// and therefore need redrawing.
bg_dirty: cell::Cell<bool>,
}
enum Placement {
@ -147,6 +151,7 @@ impl StackView {
StackView {
layers: Vec::new(),
last_size: Vec2::zero(),
bg_dirty: cell::Cell::new(true),
}
}
@ -225,6 +230,7 @@ impl StackView {
/// Remove the top-most layer.
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
self.bg_dirty.set(true);
self.layers.pop().map(|child| child.view.unwrap())
}
@ -280,6 +286,65 @@ impl StackView {
pub fn move_to_back(&mut self, layer: LayerPosition) {
self.move_layer(layer, LayerPosition::FromBack(0));
}
/// Moves a layer to a new position on the screen.
///
/// Has no effect on fullscreen layers
/// Has no effect if layer is not found
pub fn reposition_layer(
&mut self, layer: LayerPosition, position: Position
) {
let i = self.get_index(layer);
let child = match self.layers.get_mut(i) {
Some(i) => i,
None => return,
};
match child.placement {
Placement::Floating(_) => {
child.placement = Placement::Floating(position);
self.bg_dirty.set(true);
}
Placement::Fullscreen => (),
}
}
/// Background drawing
/// Drawing functions are split into forground and background to
/// ease inserting layers under the stackview but above it's background
/// you probably just want to call draw()
pub fn draw_bg(&self, printer: &Printer) {
// If the background is dirty draw a new background
if self.bg_dirty.get() {
for y in 0..printer.size.y {
printer.with_color(ColorStyle::background(), |printer| {
printer.print_hline((0, y), printer.size.x, " ");
});
}
// set background as clean, so we don't need to do this every frame
self.bg_dirty.set(false);
}
}
/// Forground drawing
/// Drawing functions are split into forground and background to
/// ease inserting layers under the stackview but above it's background
/// you probably just want to call draw()
pub fn draw_fg(&self, printer: &Printer) {
let last = self.layers.len();
printer.with_color(ColorStyle::primary(), |printer| {
for (i, (v, offset)) in
StackPositionIterator::new(self.layers.iter(), printer.size)
.enumerate()
{
v.view.draw(&printer.sub_printer(
offset,
v.size,
i + 1 == last,
));
}
});
}
}
struct StackPositionIterator<R: Deref<Target = Child>, I: Iterator<Item = R>> {
@ -325,22 +390,17 @@ impl<R: Deref<Target = Child>, I: Iterator<Item = R>> Iterator
impl View for StackView {
fn draw(&self, printer: &Printer) {
let last = self.layers.len();
printer.with_color(ColorStyle::primary(), |printer| {
for (i, (v, offset)) in
StackPositionIterator::new(self.layers.iter(), printer.size)
.enumerate()
{
v.view.draw(&printer.sub_printer(
offset,
v.size,
i + 1 == last,
));
}
});
// This function is included for compat with the view trait,
// it should behave the same as calling them seperately, but does
// not pause to let you insert in between the layers.
self.draw_bg(printer);
self.draw_fg(printer);
}
fn on_event(&mut self, event: Event) -> EventResult {
if event == Event::WindowResize {
self.bg_dirty.set(true);
}
// Use the stack position iterator to get the offset of the top layer.
// TODO: save it instead when drawing?
match StackPositionIterator::new(