Source windows from x11 server
This commit is contained in:
parent
5938bad12f
commit
fd54aeece2
8 changed files with 299 additions and 165 deletions
71
Cargo.lock
generated
71
Cargo.lock
generated
|
@ -36,9 +36,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e70cc2f62c6ce1868963827bd677764c62d07c3d9a3e1fb1177ee1a9ab199eb2"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -120,23 +120,21 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fltk"
|
||||
version = "1.1.12"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bf356f493316a45a5b90b8d5db97c0fd9850b87ab19cc7f88b53ca5d01fd710"
|
||||
checksum = "c30618861047220f552e9e7dba957bd9c0953f23d96e27447ca4a934f05870a2"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fltk-derive",
|
||||
"fltk-sys",
|
||||
"lazy_static",
|
||||
"objc",
|
||||
"raw-window-handle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fltk-derive"
|
||||
version = "1.1.12"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57ddbb2cb93484b56da41b80c1466bca2620b68b61847d5e7f999bda0782ab78"
|
||||
checksum = "36c1111ff05b7d2552093f23dd9db1e0e380f9c6c0b57d69e21811eb4057d011"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
|
@ -144,12 +142,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fltk-sys"
|
||||
version = "1.1.12"
|
||||
version = "1.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebe424d107ab6ef0a4b756295c259bb8cec6e610548d55481457309884d526d4"
|
||||
checksum = "0ccc3328371cbe9d01b89488e815c39c5680843379606aa74b1b169c1f54a489"
|
||||
dependencies = [
|
||||
"cmake",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -175,9 +172,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.100"
|
||||
version = "0.2.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
|
@ -188,15 +185,6 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "malloc_buf"
|
||||
version = "0.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
|
@ -222,20 +210,11 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
|
||||
dependencies = [
|
||||
"malloc_buf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.28"
|
||||
version = "1.0.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||
checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -308,6 +287,8 @@ dependencies = [
|
|||
"fltk",
|
||||
"log",
|
||||
"rayon",
|
||||
"xcb",
|
||||
"xcb-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -318,9 +299,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.75"
|
||||
version = "1.0.77"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7f58f7e8eaa0009c5fec437aabf511bd9933e4b2d7407bd05273c01a8906ea7"
|
||||
checksum = "5239bc68e0fef57495900cfea4e8dc75596d9a319d7e16b1e0a440d24e6fe0a0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -372,3 +353,23 @@ name = "winapi-x86_64-pc-windows-gnu"
|
|||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xcb"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62056f63138b39116f82a540c983cc11f1c90cd70b3d492a70c25eaa50bd22a6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xcb-util"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43893e47f27bf7d81d489feef3a0e34a457e90bc314b7e74ad9bb3980e4c1c48"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"xcb",
|
||||
]
|
||||
|
|
|
@ -9,4 +9,7 @@ edition = "2018"
|
|||
fltk = "1.1"
|
||||
log = "0.4"
|
||||
env_logger = "0.9.0"
|
||||
rayon = "1.5.1"
|
||||
rayon = "1.5.1"
|
||||
|
||||
xcb = "0.9.0"
|
||||
xcb-util = {version = "0.3.0", features = ["ewmh", "icccm"]}
|
67
src/main.rs
67
src/main.rs
|
@ -3,66 +3,15 @@ use std::error::Error;
|
|||
|
||||
mod roftl;
|
||||
mod ui;
|
||||
|
||||
use roftl::{Action, Entry, Source};
|
||||
|
||||
struct TestSource {
|
||||
name: String,
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl TestSource {
|
||||
fn new(s: &str) -> Box<TestSource> {
|
||||
let mut ts = Box::new(TestSource {
|
||||
name: "Testsource".into(),
|
||||
entries: vec![],
|
||||
});
|
||||
|
||||
(1..1000).for_each(|i| {
|
||||
ts.add_entry(
|
||||
format! {"Test {} {}", i, s},
|
||||
format! {"Test {} description", i},
|
||||
);
|
||||
});
|
||||
|
||||
ts
|
||||
}
|
||||
|
||||
fn add_entry(&mut self, name: String, description: String) {
|
||||
let id = self.entries.len();
|
||||
self.entries.push(Entry {
|
||||
name,
|
||||
description,
|
||||
source: self,
|
||||
id: id as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Source for TestSource {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn entries(&self) -> Vec<Entry> {
|
||||
self.entries.clone()
|
||||
}
|
||||
|
||||
fn action(&self, entry: &Entry, action: Action) {
|
||||
println!("Doing action {:?} for entry {}", action, entry.name)
|
||||
}
|
||||
}
|
||||
mod sources;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
|
||||
let mut roftl = roftl::Roftl::new();
|
||||
|
||||
roftl
|
||||
.add_source(TestSource::new("ts1"))
|
||||
.add_source(TestSource::new("ts2"));
|
||||
|
||||
roftl.source();
|
||||
let roftl = roftl::Roftl::default()
|
||||
.add_source(sources::TestSource::new("ts1"))
|
||||
.add_source(sources::Window::new())
|
||||
.source();
|
||||
|
||||
let mut app = ui::draw()?;
|
||||
|
||||
|
@ -73,9 +22,9 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
while app.wait() {
|
||||
match app.events.recv() {
|
||||
Some(ui::Event::CandidateSelect(entry)) => {
|
||||
trace! {"Selected: {}", entry.name}
|
||||
roftl.action(&entry, Action::Primary)
|
||||
Some(ui::Event::CandidateSelect(i)) => {
|
||||
println! {"Selected an entry {}", i}
|
||||
roftl.action(i, roftl::Action::Primary)
|
||||
}
|
||||
|
||||
Some(ui::Event::InputUpdate) => {
|
||||
|
|
67
src/roftl.rs
67
src/roftl.rs
|
@ -1,63 +1,82 @@
|
|||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Entry {
|
||||
pub source: &'static str,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
pub source: *const dyn Source,
|
||||
pub id: u32
|
||||
pub identifier: u64,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Entry {}
|
||||
unsafe impl Send for Entry {}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Action {
|
||||
Primary,
|
||||
}
|
||||
|
||||
pub trait Source: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
fn entries(&self) -> Vec<Entry>;
|
||||
fn name(&self) -> &'static str;
|
||||
fn entries(&mut self) -> Vec<Entry>;
|
||||
fn action(&self, entry: &Entry, action: Action);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Roftl {
|
||||
sources: Vec<Box<dyn Source>>,
|
||||
entries: Vec<Entry>,
|
||||
narrow_map: Mutex<HashMap<usize, usize>>
|
||||
}
|
||||
|
||||
impl Roftl {
|
||||
pub fn new() -> Roftl {
|
||||
Roftl {
|
||||
sources: vec![],
|
||||
entries: vec![],
|
||||
pub fn add_source(mut self, source: Box<dyn Source>) -> Self {
|
||||
if self.sources.par_iter().any(|s| s.name().eq(source.name())) {
|
||||
panic! {"Source with name '{}' already exists", source.name()}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_source(&mut self, source: Box<dyn Source>) -> &mut Roftl {
|
||||
self.sources.push(source);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn source(&mut self) {
|
||||
pub fn source(mut self) -> Self {
|
||||
self.entries = self
|
||||
.sources
|
||||
.par_iter()
|
||||
.par_iter_mut()
|
||||
.flat_map_iter(|s| s.entries())
|
||||
.collect();
|
||||
|
||||
println!("Entries {:?}", self.entries);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn narrow(&self, input: &str) -> Vec<&Entry> {
|
||||
self.entries.par_iter()
|
||||
.filter(|e| e.name.starts_with(input))
|
||||
let count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
self.narrow_map.lock().unwrap().clear();
|
||||
|
||||
self.entries
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.filter(|(_idx, e)| e.name.starts_with(input))
|
||||
.map(move |(idx, e)| {
|
||||
self.narrow_map.lock().unwrap().insert(count.load(std::sync::atomic::Ordering::Acquire), idx);
|
||||
count.fetch_add(1, std::sync::atomic::Ordering::Acquire);
|
||||
e
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn action(&self, entry: &Entry, action: Action) {
|
||||
unsafe {
|
||||
Source::action(&*entry.source, entry, action);
|
||||
}
|
||||
pub fn action(&self, entry_id: i32, action: Action) {
|
||||
println!("NarrowMap {:?}", self.narrow_map.lock().unwrap());
|
||||
|
||||
let real_id = *self.narrow_map.lock().unwrap().get(&((entry_id-1) as usize)).unwrap();
|
||||
let entry = self.entries.get(real_id).unwrap();
|
||||
|
||||
let source = self
|
||||
.sources
|
||||
.iter()
|
||||
.find(|&s| s.name().eq(entry.source))
|
||||
.unwrap();
|
||||
|
||||
source.action(entry, action);
|
||||
}
|
||||
}
|
||||
|
|
6
src/sources/mod.rs
Normal file
6
src/sources/mod.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
mod test;
|
||||
pub use test::TestSource;
|
||||
|
||||
mod windows;
|
||||
pub use windows::Window;
|
||||
|
45
src/sources/test.rs
Normal file
45
src/sources/test.rs
Normal file
|
@ -0,0 +1,45 @@
|
|||
use crate::roftl::{Entry, Source, Action};
|
||||
|
||||
pub struct TestSource {
|
||||
entries: Vec<Entry>,
|
||||
}
|
||||
|
||||
impl TestSource {
|
||||
pub fn new(s: &str) -> Box<TestSource> {
|
||||
let mut ts = Box::new(TestSource { entries: vec![] });
|
||||
|
||||
(1..2).for_each(|i| {
|
||||
ts.add_entry(
|
||||
format! {"Test {} {}", i, s},
|
||||
format! {"Test {} description", i},
|
||||
);
|
||||
});
|
||||
|
||||
ts
|
||||
}
|
||||
|
||||
pub fn add_entry(&mut self, name: String, description: String) {
|
||||
let id = self.entries.len();
|
||||
self.entries.push(Entry {
|
||||
name,
|
||||
description,
|
||||
source: self.name(),
|
||||
identifier: id as u64,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Source for TestSource {
|
||||
fn name(&self) -> &'static str {
|
||||
"TestSource"
|
||||
}
|
||||
|
||||
fn entries(&mut self) -> Vec<Entry> {
|
||||
self.entries.clone()
|
||||
}
|
||||
|
||||
fn action(&self, entry: &Entry, action: Action) {
|
||||
println!("Doing action {:?} for entry {}", action, entry.name)
|
||||
}
|
||||
}
|
||||
|
97
src/sources/windows.rs
Normal file
97
src/sources/windows.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
use std::collections::HashMap;
|
||||
use std::ptr;
|
||||
|
||||
use crate::roftl::{Entry, Source, Action};
|
||||
use xcb::ffi::XCB_CURRENT_TIME;
|
||||
use xcb_util::ewmh;
|
||||
use xcb_util::ffi::ewmh::XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER;
|
||||
use xcb_util::icccm;
|
||||
|
||||
pub struct Window {
|
||||
windows: Vec<String>,
|
||||
action_data: HashMap<u64, (u32, i32)>
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub fn new() -> Box<Window> {
|
||||
Box::new(Window { windows: vec![], action_data: HashMap::<u64, (u32,i32)>::new() })
|
||||
}
|
||||
|
||||
fn is_normal_window(&self, con: &ewmh::Connection, window: u32) -> Result<bool, xcb::GenericError> {
|
||||
let wm_type_reply = ewmh::get_wm_window_type(con, window).get_reply()?;
|
||||
for atom in wm_type_reply.atoms() {
|
||||
if *atom == con.WM_WINDOW_TYPE_NORMAL() {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
Err(xcb::GenericError{ptr: ptr::null_mut()})
|
||||
}
|
||||
|
||||
fn window_category(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::GenericError> {
|
||||
Ok(icccm::get_wm_class(&con, window).get_reply()?.class().into())
|
||||
}
|
||||
|
||||
fn window_title(&self, con: &ewmh::Connection, window: u32) -> Result<String, xcb::GenericError> {
|
||||
Ok(ewmh::get_wm_name(con, window).get_reply()?.string().into())
|
||||
}
|
||||
|
||||
fn switch_window(&self, con: &ewmh::Connection, window: u32, screen: i32) -> Result<(), xcb::GenericError> {
|
||||
let window_desktop = ewmh::get_wm_desktop(con, window).get_reply()?;
|
||||
let active_window = ewmh::get_active_window(con, screen).get_reply()?;
|
||||
ewmh::set_current_desktop(con, screen, window_desktop);
|
||||
ewmh::request_change_active_window(con, screen, window, XCB_EWMH_CLIENT_SOURCE_TYPE_OTHER, XCB_CURRENT_TIME, active_window).request_check().unwrap();
|
||||
ewmh::set_active_window(con, screen, window).request_check().unwrap();
|
||||
xcb::set_input_focus(&con, 0, window, XCB_CURRENT_TIME);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Source for Window {
|
||||
fn name(&self) -> &'static str {
|
||||
"window"
|
||||
}
|
||||
|
||||
fn entries(&mut self) -> Vec<Entry> {
|
||||
let (xcb_conn, screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
||||
|
||||
let client_list_reply = xcb_util::ewmh::get_client_list(&ewmh_conn, screen_num).get_reply().unwrap();
|
||||
let windows = client_list_reply.windows();
|
||||
|
||||
let mut entries: Vec<Entry> = vec![];
|
||||
|
||||
let mut count: u64 = 0;
|
||||
for w in windows {
|
||||
match self.is_normal_window(&ewmh_conn, *w) {
|
||||
Ok(true) => {},
|
||||
Ok(false) => continue,
|
||||
Err(_err) => continue
|
||||
}
|
||||
|
||||
let title = self.window_title(&ewmh_conn, *w).unwrap();
|
||||
let category = self.window_category(&ewmh_conn, *w).unwrap();
|
||||
|
||||
println!("Found window {} - {}", title, category);
|
||||
|
||||
entries.push(Entry{source: self.name(), name: category, description: title, identifier: count});
|
||||
self.action_data.insert(count, (*w, screen_num));
|
||||
|
||||
count+=1;
|
||||
}
|
||||
|
||||
entries
|
||||
}
|
||||
|
||||
fn action(&self, entry: &Entry, action: Action) {
|
||||
println!("Doing action {:?} for entry {}", action, entry.name);
|
||||
|
||||
let (window, screen) = *self.action_data.get(&entry.identifier).unwrap();
|
||||
|
||||
let (xcb_conn, _screen_num) = xcb::base::Connection::connect(None).expect("Could not connect to X server");
|
||||
let ewmh_conn = ewmh::Connection::connect(xcb_conn).map_err(|(e,_)| e).unwrap();
|
||||
|
||||
self.switch_window(&ewmh_conn, window, screen).unwrap()
|
||||
}
|
||||
}
|
106
src/ui.rs
106
src/ui.rs
|
@ -1,58 +1,37 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use fltk::enums::{CallbackTrigger, Font, FrameType};
|
||||
use fltk::app::event_key;
|
||||
use fltk::enums::{CallbackTrigger, Font, FrameType, Key};
|
||||
use fltk::group::{PackType, ScrollType};
|
||||
use fltk::output;
|
||||
use fltk::prelude::*;
|
||||
use fltk::{app, window, input, group};
|
||||
use fltk::{app, group, input, window};
|
||||
use fltk::{browser, output};
|
||||
|
||||
use super::roftl::Entry;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Event {
|
||||
InputUpdate,
|
||||
CandidateSelect(Entry)
|
||||
CandidateSelect(i32),
|
||||
}
|
||||
|
||||
pub struct RoftlApp {
|
||||
pub app: app::App,
|
||||
pub events: app::Receiver::<Event>,
|
||||
pub events: app::Receiver<Event>,
|
||||
|
||||
send: app::Sender<Event>,
|
||||
input: input::Input,
|
||||
candidates: group::Pack,
|
||||
scroll: group::Scroll
|
||||
browser: browser::HoldBrowser,
|
||||
}
|
||||
|
||||
impl RoftlApp {
|
||||
pub fn add_candidate(&mut self, entry: Entry) {
|
||||
let mut output = output::Output::default();
|
||||
output.set_value(&entry.name);
|
||||
output.set_frame(FrameType::FlatBox);
|
||||
output.set_size(380,30);
|
||||
|
||||
let s = self.send.clone();
|
||||
output.handle(move |_widget, ev: fltk::enums::Event| {
|
||||
match ev {
|
||||
fltk::enums::Event::Focus => {
|
||||
s.send(Event::CandidateSelect(entry.clone()));
|
||||
true
|
||||
},
|
||||
|
||||
_ => false
|
||||
}
|
||||
});
|
||||
|
||||
self.candidates.add(&output);
|
||||
self.scroll.redraw();
|
||||
let entry_line = format! {"{}\tf{}\t{}", entry.name, entry.description, entry.source};
|
||||
self.browser.add(&entry_line);
|
||||
}
|
||||
|
||||
pub fn clear_candidates(&mut self) {
|
||||
for i in 0..self.candidates.children() {
|
||||
self.candidates.child(i).and_then(|c| {Some(WidgetBase::delete(c))});
|
||||
}
|
||||
|
||||
self.scroll.redraw();
|
||||
self.browser.clear();
|
||||
}
|
||||
|
||||
pub fn get_input(&self) -> String {
|
||||
|
@ -69,7 +48,6 @@ impl Deref for RoftlApp {
|
|||
}
|
||||
|
||||
pub fn draw() -> Result<RoftlApp, FltkError> {
|
||||
|
||||
app::set_font(Font::Screen);
|
||||
|
||||
let app = app::App::default();
|
||||
|
@ -81,30 +59,66 @@ pub fn draw() -> Result<RoftlApp, FltkError> {
|
|||
|
||||
let pack = group::Pack::default()
|
||||
.with_type(PackType::Vertical)
|
||||
.with_size(400,40);
|
||||
.with_size(400, 40);
|
||||
|
||||
let mut input = input::Input::default()
|
||||
.with_size(0,40);
|
||||
let mut input = input::Input::default().with_size(0, 40);
|
||||
input.set_trigger(CallbackTrigger::Changed);
|
||||
|
||||
let scroll = group::Scroll::default()
|
||||
.with_size(400, 300-40)
|
||||
.with_type(ScrollType::AlwaysOn);
|
||||
let mut browser: browser::HoldBrowser =
|
||||
browser::HoldBrowser::default().with_size(400, 300 - 40);
|
||||
browser.set_has_scrollbar(browser::BrowserScrollbar::None);
|
||||
|
||||
let candidates = group::Pack::default()
|
||||
.with_size(380, 300-40)
|
||||
.center_of(&scroll);
|
||||
|
||||
candidates.end();
|
||||
scroll.end();
|
||||
pack.end();
|
||||
window.end();
|
||||
|
||||
window.show();
|
||||
|
||||
let (s, r) = app::channel::<Event>();
|
||||
|
||||
input.emit(s.clone(), Event::InputUpdate);
|
||||
|
||||
Ok(RoftlApp{app, events: r, send: s, input, candidates, scroll})
|
||||
}
|
||||
let mut b1 = browser.clone();
|
||||
let s1 = s.clone();
|
||||
input.handle(move |_input, evt| match evt {
|
||||
fltk::enums::Event::KeyUp => {
|
||||
if event_key().eq(&Key::Tab) {
|
||||
b1.take_focus().unwrap();
|
||||
b1.select(1);
|
||||
s1.send(Event::CandidateSelect(b1.value()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_ => false,
|
||||
});
|
||||
|
||||
let mut b2 = browser.clone();
|
||||
let s2 = s.clone();
|
||||
browser.handle(move |_input, evt| match evt {
|
||||
fltk::enums::Event::KeyUp => {
|
||||
if event_key().eq(&Key::Tab) {
|
||||
b2.take_focus().unwrap();
|
||||
b2.select(b2.value() + 1);
|
||||
s2.send(Event::CandidateSelect(b2.value()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_ => false,
|
||||
});
|
||||
|
||||
let s3 = s.clone();
|
||||
browser.set_callback(move |b| {
|
||||
s3.send(Event::CandidateSelect(b.value()));
|
||||
});
|
||||
|
||||
Ok(RoftlApp {
|
||||
app,
|
||||
events: r,
|
||||
send: s,
|
||||
input,
|
||||
browser,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue