Produce mouse events from BLT backend

This commit is contained in:
Alexandre Bury 2017-10-13 00:20:27 -07:00
parent acd12326e1
commit 5f3e4b1842

View File

@ -5,147 +5,25 @@ use self::bear_lib_terminal::geometry::Size;
use self::bear_lib_terminal::terminal::{self, state, Event as BltEvent, use self::bear_lib_terminal::terminal::{self, state, Event as BltEvent,
KeyCode}; KeyCode};
use backend; use backend;
use event::{Event, Key}; use event::{Event, Key, MouseButton, MouseEvent};
use std::collections::BTreeMap;
use theme::{BaseColor, Color, ColorPair, Effect}; use theme::{BaseColor, Color, ColorPair, Effect};
use vec::Vec2;
use std::collections::HashSet;
enum ColorRole { enum ColorRole {
Foreground, Foreground,
Background, Background,
} }
pub struct Concrete {} pub struct Concrete {
mouse_position: Vec2,
impl backend::Backend for Concrete { buttons_pressed: HashSet<MouseButton>,
fn init() -> Self {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
Concrete {}
}
fn finish(&mut self) {
terminal::close();
}
fn with_color<F: FnOnce()>(&self, color: ColorPair, f: F) {
let fg = colour_to_blt_colour(color.front, ColorRole::Foreground);
let bg = colour_to_blt_colour(color.back, ColorRole::Background);
terminal::with_colors(fg, bg, f);
}
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
match effect {
Effect::Simple => f(),
// TODO: how to do this correctly?`
// BLT itself doesn't do this kind of thing,
// we'd need the colours in our position,
// but `f()` can do whatever
Effect::Reverse => terminal::with_colors(
BltColor::from_rgb(0, 0, 0),
BltColor::from_rgb(255, 255, 255),
f,
),
}
}
fn has_colors(&self) -> bool {
true
}
fn screen_size(&self) -> (usize, usize) {
let Size { width, height } = terminal::state::size();
(width as usize, height as usize)
}
fn clear(&self, color: Color) {
terminal::set_background(
colour_to_blt_colour(color, ColorRole::Background),
);
terminal::clear(None);
}
fn refresh(&mut self) {
terminal::refresh();
}
fn print_at(&self, (x, y): (usize, usize), text: &str) {
terminal::print_xy(x as i32, y as i32, text);
}
fn set_refresh_rate(&mut self, _: u32) {
// TODO: unsupported
}
fn poll_event(&mut self) -> Event {
// TODO: we could add backend-specific controls here.
// Ex: ctrl+mouse wheel cause window cellsize to change
if let Some(ev) = terminal::wait_event() {
match ev {
BltEvent::Close => Event::Exit,
BltEvent::Resize { .. } => Event::WindowResize,
// TODO: mouse support
BltEvent::MouseMove { .. } => Event::Refresh,
BltEvent::MouseScroll { .. } => Event::Refresh,
BltEvent::KeyPressed { key, ctrl, shift } => {
blt_keycode_to_ev(key, shift, ctrl)
}
// TODO: there's no Key::Shift/Ctrl for w/e reason
BltEvent::ShiftPressed => Event::Refresh,
BltEvent::ControlPressed => Event::Refresh,
// TODO: what should we do here?
BltEvent::KeyReleased { .. } |
BltEvent::ShiftReleased |
BltEvent::ControlReleased => Event::Refresh,
}
} else {
Event::Refresh
}
}
} }
fn colour_to_blt_colour(clr: Color, role: ColorRole) -> BltColor { impl Concrete {
let (r, g, b) = match clr { fn blt_keycode_to_ev(
Color::TerminalDefault => { &mut self, kc: KeyCode, shift: bool, ctrl: bool
let clr = match role { ) -> Event {
ColorRole::Foreground => state::foreground(),
ColorRole::Background => state::background(),
};
return clr;
}
// Colours taken from
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
Color::Dark(BaseColor::Black) => (0, 0, 0),
Color::Dark(BaseColor::Red) => (170, 0, 0),
Color::Dark(BaseColor::Green) => (0, 170, 0),
Color::Dark(BaseColor::Yellow) => (170, 85, 0),
Color::Dark(BaseColor::Blue) => (0, 0, 170),
Color::Dark(BaseColor::Magenta) => (170, 0, 170),
Color::Dark(BaseColor::Cyan) => (0, 170, 170),
Color::Dark(BaseColor::White) => (170, 170, 170),
Color::Light(BaseColor::Black) => (85, 85, 85),
Color::Light(BaseColor::Red) => (255, 85, 85),
Color::Light(BaseColor::Green) => (85, 255, 85),
Color::Light(BaseColor::Yellow) => (255, 255, 85),
Color::Light(BaseColor::Blue) => (85, 85, 255),
Color::Light(BaseColor::Magenta) => (255, 85, 255),
Color::Light(BaseColor::Cyan) => (85, 255, 255),
Color::Light(BaseColor::White) => (255, 255, 255),
Color::Rgb(r, g, b) => (r, g, b),
Color::RgbLowRes(r, g, b) => (
(r as f32 / 5.0 * 255.0) as u8,
(g as f32 / 5.0 * 255.0) as u8,
(b as f32 / 5.0 * 255.0) as u8,
),
};
BltColor::from_rgb(r, g, b)
}
fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
match kc { match kc {
KeyCode::F1 | KeyCode::F1 |
KeyCode::F2 | KeyCode::F2 |
@ -185,7 +63,16 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
KeyCode::MouseRight | KeyCode::MouseRight |
KeyCode::MouseMiddle | KeyCode::MouseMiddle |
KeyCode::MouseFourth | KeyCode::MouseFourth |
KeyCode::MouseFifth => Event::Refresh, KeyCode::MouseFifth => blt_keycode_to_mouse_button(kc)
.map(|btn| {
self.buttons_pressed.insert(btn);
Event::Mouse {
event: MouseEvent::Press(btn),
position: self.mouse_position,
offset: Vec2::zero(),
}
})
.unwrap_or(Event::Unknown(vec![])),
KeyCode::A | KeyCode::A |
KeyCode::B | KeyCode::B |
KeyCode::C | KeyCode::C |
@ -254,6 +141,181 @@ fn blt_keycode_to_ev(kc: KeyCode, shift: bool, ctrl: bool) -> Event {
Event::Char(blt_keycode_to_char(kc, shift)) Event::Char(blt_keycode_to_char(kc, shift))
}, },
} }
}
}
impl backend::Backend for Concrete {
fn init() -> Self {
terminal::open("Cursive", 80, 24);
terminal::set(terminal::config::Window::empty().resizeable(true));
terminal::set(vec![
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Keyboard,
both: false,
},
terminal::config::InputFilter::Group {
group: terminal::config::InputFilterGroup::Mouse,
both: true,
},
]);
Concrete {
mouse_position: Vec2::zero(),
buttons_pressed: HashSet::new(),
}
}
fn finish(&mut self) {
terminal::close();
}
fn with_color<F: FnOnce()>(&self, color: ColorPair, f: F) {
let fg = colour_to_blt_colour(color.front, ColorRole::Foreground);
let bg = colour_to_blt_colour(color.back, ColorRole::Background);
terminal::with_colors(fg, bg, f);
}
fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
match effect {
Effect::Simple => f(),
// TODO: how to do this correctly?`
// BLT itself doesn't do this kind of thing,
// we'd need the colours in our position,
// but `f()` can do whatever
Effect::Reverse => terminal::with_colors(
BltColor::from_rgb(0, 0, 0),
BltColor::from_rgb(255, 255, 255),
f,
),
}
}
fn has_colors(&self) -> bool {
true
}
fn screen_size(&self) -> (usize, usize) {
let Size { width, height } = terminal::state::size();
(width as usize, height as usize)
}
fn clear(&self, color: Color) {
terminal::set_background(
colour_to_blt_colour(color, ColorRole::Background),
);
terminal::clear(None);
}
fn refresh(&mut self) {
terminal::refresh();
}
fn print_at(&self, (x, y): (usize, usize), text: &str) {
terminal::print_xy(x as i32, y as i32, text);
}
fn set_refresh_rate(&mut self, _: u32) {
// TODO: unsupported
}
fn poll_event(&mut self) -> Event {
// TODO: we could add backend-specific controls here.
// Ex: ctrl+mouse wheel cause window cellsize to change
if let Some(ev) = terminal::wait_event() {
match ev {
BltEvent::Close => Event::Exit,
BltEvent::Resize { .. } => Event::WindowResize,
// TODO: mouse support
BltEvent::MouseMove { x, y } => {
self.mouse_position = Vec2::new(x as usize, y as usize);
// TODO: find out if a button is pressed?
match self.buttons_pressed.iter().next() {
None => Event::Refresh,
Some(btn) => Event::Mouse {
event: MouseEvent::Hold(*btn),
position: self.mouse_position,
offset: Vec2::zero(),
}
}
}
BltEvent::MouseScroll { delta } => Event::Mouse {
event: if delta < 0 {
MouseEvent::WheelUp
} else {
MouseEvent::WheelDown
},
position: self.mouse_position,
offset: Vec2::zero(),
},
BltEvent::KeyPressed { key, ctrl, shift } => {
self.blt_keycode_to_ev(key, shift, ctrl)
}
// TODO: there's no Key::Shift/Ctrl for w/e reason
BltEvent::ShiftPressed => Event::Refresh,
BltEvent::ControlPressed => Event::Refresh,
// TODO: what should we do here?
BltEvent::KeyReleased { key, .. } => {
// It's probably a mouse key.
blt_keycode_to_mouse_button(key)
.map(|btn| {
self.buttons_pressed.remove(&btn);
Event::Mouse {
event: MouseEvent::Release(btn),
position: self.mouse_position,
offset: Vec2::zero(),
}
})
.unwrap_or(Event::Unknown(vec![]))
}
BltEvent::ShiftReleased | BltEvent::ControlReleased => {
Event::Refresh
}
}
} else {
Event::Refresh
}
}
}
fn colour_to_blt_colour(clr: Color, role: ColorRole) -> BltColor {
let (r, g, b) = match clr {
Color::TerminalDefault => {
let clr = match role {
ColorRole::Foreground => state::foreground(),
ColorRole::Background => state::background(),
};
return clr;
}
// Colours taken from
// https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
Color::Dark(BaseColor::Black) => (0, 0, 0),
Color::Dark(BaseColor::Red) => (170, 0, 0),
Color::Dark(BaseColor::Green) => (0, 170, 0),
Color::Dark(BaseColor::Yellow) => (170, 85, 0),
Color::Dark(BaseColor::Blue) => (0, 0, 170),
Color::Dark(BaseColor::Magenta) => (170, 0, 170),
Color::Dark(BaseColor::Cyan) => (0, 170, 170),
Color::Dark(BaseColor::White) => (170, 170, 170),
Color::Light(BaseColor::Black) => (85, 85, 85),
Color::Light(BaseColor::Red) => (255, 85, 85),
Color::Light(BaseColor::Green) => (85, 255, 85),
Color::Light(BaseColor::Yellow) => (255, 255, 85),
Color::Light(BaseColor::Blue) => (85, 85, 255),
Color::Light(BaseColor::Magenta) => (255, 85, 255),
Color::Light(BaseColor::Cyan) => (85, 255, 255),
Color::Light(BaseColor::White) => (255, 255, 255),
Color::Rgb(r, g, b) => (r, g, b),
Color::RgbLowRes(r, g, b) => (
(r as f32 / 5.0 * 255.0) as u8,
(g as f32 / 5.0 * 255.0) as u8,
(b as f32 / 5.0 * 255.0) as u8,
),
};
BltColor::from_rgb(r, g, b)
} }
fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char { fn blt_keycode_to_char(kc: KeyCode, shift: bool) -> char {
@ -545,3 +607,12 @@ fn blt_keycode_to_key(kc: KeyCode) -> Key {
_ => unreachable!(), _ => unreachable!(),
} }
} }
fn blt_keycode_to_mouse_button(kc: KeyCode) -> Option<MouseButton> {
Some(match kc {
KeyCode::MouseLeft => MouseButton::Left,
KeyCode::MouseRight => MouseButton::Right,
KeyCode::MouseMiddle => MouseButton::Middle,
_ => return None,
})
}