mirror of
https://github.com/FliegendeWurst/cursive.git
synced 2024-11-23 17:35:00 +00:00
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:
parent
22e2360aa1
commit
9e591ef635
56
examples/position.rs
Normal file
56
examples/position.rs
Normal 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();
|
||||||
|
}
|
@ -9,8 +9,8 @@ use std::path::Path;
|
|||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use theme;
|
use theme;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
use view::{self, AnyView, Finder, View};
|
use view::{self, AnyView, Finder, Position, View};
|
||||||
use views;
|
use views::{self, LayerPosition};
|
||||||
|
|
||||||
/// Identifies a screen in the cursive root.
|
/// Identifies a screen in the cursive root.
|
||||||
pub type ScreenId = usize;
|
pub type ScreenId = usize;
|
||||||
@ -416,9 +416,14 @@ impl Cursive {
|
|||||||
|
|
||||||
/// Convenient method to remove a layer from the current screen.
|
/// Convenient method to remove a layer from the current screen.
|
||||||
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
||||||
let result = self.screen_mut().pop_layer();
|
self.screen_mut().pop_layer()
|
||||||
self.clear();
|
}
|
||||||
result
|
|
||||||
|
/// 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
|
// Handles a key event when it was ignored by the current view
|
||||||
@ -460,6 +465,15 @@ impl Cursive {
|
|||||||
let printer =
|
let printer =
|
||||||
Printer::new(self.screen_size(), &self.theme, &self.backend);
|
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
|
// Draw the currently active screen
|
||||||
// If the menubar is active, nothing else can be.
|
// If the menubar is active, nothing else can be.
|
||||||
// Draw the menubar?
|
// Draw the menubar?
|
||||||
@ -472,12 +486,9 @@ impl Cursive {
|
|||||||
self.menubar.draw(&printer);
|
self.menubar.draw(&printer);
|
||||||
}
|
}
|
||||||
|
|
||||||
let selected = self.menubar.receive_events();
|
// finally draw stackview layers
|
||||||
|
// using variables from above
|
||||||
let offset = if self.menubar.autohide { 0 } else { 1 };
|
self.screens[id].draw_fg(&sv_printer);
|
||||||
let printer = printer.offset((0, offset), !selected);
|
|
||||||
let id = self.active_screen;
|
|
||||||
self.screens[id].draw(&printer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns `true` until [`quit(&mut self)`] is called.
|
/// Returns `true` until [`quit(&mut self)`] is called.
|
||||||
|
@ -3,6 +3,7 @@ use With;
|
|||||||
use direction::Direction;
|
use direction::Direction;
|
||||||
use event::{Event, EventResult};
|
use event::{Event, EventResult};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
use std::cell;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use theme::ColorStyle;
|
use theme::ColorStyle;
|
||||||
use vec::Vec2;
|
use vec::Vec2;
|
||||||
@ -15,6 +16,9 @@ pub struct StackView {
|
|||||||
// Store layers from back to front.
|
// Store layers from back to front.
|
||||||
layers: Vec<Child>,
|
layers: Vec<Child>,
|
||||||
last_size: Vec2,
|
last_size: Vec2,
|
||||||
|
// Flag indicates if undrawn areas of the background are exposed
|
||||||
|
// and therefore need redrawing.
|
||||||
|
bg_dirty: cell::Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Placement {
|
enum Placement {
|
||||||
@ -147,6 +151,7 @@ impl StackView {
|
|||||||
StackView {
|
StackView {
|
||||||
layers: Vec::new(),
|
layers: Vec::new(),
|
||||||
last_size: Vec2::zero(),
|
last_size: Vec2::zero(),
|
||||||
|
bg_dirty: cell::Cell::new(true),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +230,7 @@ impl StackView {
|
|||||||
|
|
||||||
/// Remove the top-most layer.
|
/// Remove the top-most layer.
|
||||||
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
pub fn pop_layer(&mut self) -> Option<Box<AnyView>> {
|
||||||
|
self.bg_dirty.set(true);
|
||||||
self.layers.pop().map(|child| child.view.unwrap())
|
self.layers.pop().map(|child| child.view.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,6 +286,65 @@ impl StackView {
|
|||||||
pub fn move_to_back(&mut self, layer: LayerPosition) {
|
pub fn move_to_back(&mut self, layer: LayerPosition) {
|
||||||
self.move_layer(layer, LayerPosition::FromBack(0));
|
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>> {
|
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 {
|
impl View for StackView {
|
||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
let last = self.layers.len();
|
// This function is included for compat with the view trait,
|
||||||
printer.with_color(ColorStyle::primary(), |printer| {
|
// it should behave the same as calling them seperately, but does
|
||||||
for (i, (v, offset)) in
|
// not pause to let you insert in between the layers.
|
||||||
StackPositionIterator::new(self.layers.iter(), printer.size)
|
self.draw_bg(printer);
|
||||||
.enumerate()
|
self.draw_fg(printer);
|
||||||
{
|
|
||||||
v.view.draw(&printer.sub_printer(
|
|
||||||
offset,
|
|
||||||
v.size,
|
|
||||||
i + 1 == last,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(&mut self, event: Event) -> EventResult {
|
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.
|
// Use the stack position iterator to get the offset of the top layer.
|
||||||
// TODO: save it instead when drawing?
|
// TODO: save it instead when drawing?
|
||||||
match StackPositionIterator::new(
|
match StackPositionIterator::new(
|
||||||
|
Loading…
Reference in New Issue
Block a user