Compare commits
5 Commits
93bcda43d8
...
cabb4a9fa6
Author | SHA1 | Date |
---|---|---|
mars | cabb4a9fa6 | |
mars | 46b8374de2 | |
mars | 3f79394186 | |
mars | 97c2c6c070 | |
mars | a4901bf136 |
83
src/main.rs
83
src/main.rs
|
@ -1,4 +1,6 @@
|
|||
use clap::Parser;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use cursive::Cursive;
|
||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||
use protocol::*;
|
||||
use protocol_derive::{Decode, Encode};
|
||||
|
@ -73,21 +75,34 @@ pub struct Room {
|
|||
|
||||
pub struct App {
|
||||
args: Args,
|
||||
tui: tui::Tui,
|
||||
socket: UdpSocket,
|
||||
cursive: Cursive,
|
||||
owned_rooms: HashMap<String, Room>,
|
||||
remote_rooms: HashMap<String, Room>,
|
||||
message_sender: Sender<String>,
|
||||
message_receiver: Receiver<String>,
|
||||
// TODO connection management
|
||||
other: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub fn new(args: Args) -> Self {
|
||||
let socket = UdpSocket::bind(args.bind_addr).unwrap();
|
||||
socket.set_nonblocking(true).unwrap();
|
||||
|
||||
let (message_sender, message_receiver) = crossbeam_channel::unbounded();
|
||||
|
||||
let cursive = tui::make_cursive(message_sender.to_owned());
|
||||
|
||||
let mut app = Self {
|
||||
args,
|
||||
tui: tui::Tui::new(),
|
||||
socket,
|
||||
cursive,
|
||||
owned_rooms: Default::default(),
|
||||
remote_rooms: Default::default(),
|
||||
socket,
|
||||
message_sender,
|
||||
message_receiver,
|
||||
other: None,
|
||||
};
|
||||
app.startup();
|
||||
app
|
||||
|
@ -111,28 +126,56 @@ impl App {
|
|||
}
|
||||
|
||||
pub fn run(mut self) {
|
||||
self.tui.run();
|
||||
let mut siv = tui::make_cursive(self.message_sender.to_owned());
|
||||
let siv_backend = cursive::backends::try_default().unwrap();
|
||||
let mut siv_runner = siv.runner(siv_backend);
|
||||
siv_runner.refresh();
|
||||
|
||||
let mut buf = [0u8; 65507];
|
||||
while let Ok((len, from)) = self.socket.recv_from(&mut buf) {
|
||||
let mut buf = buf.as_slice();
|
||||
println!("recv'd from {:?}: {:?}", from, &buf[..len]);
|
||||
while siv_runner.is_running() {
|
||||
siv_runner.step();
|
||||
|
||||
let kind = Var::<u16>::decode(&mut buf).unwrap().0;
|
||||
let kind: PacketKind = match kind.try_into() {
|
||||
Ok(kind) => kind,
|
||||
Err(int) => {
|
||||
eprintln!("unrecognized packet kind {}", int);
|
||||
continue;
|
||||
let mut buf = [0u8; 65507];
|
||||
// TODO error handling of non-non-blocking errors
|
||||
if let Ok((len, from)) = self.socket.recv_from(&mut buf) {
|
||||
let mut buf = buf.as_slice();
|
||||
|
||||
let kind = Var::<u16>::decode(&mut buf).unwrap().0;
|
||||
let kind: PacketKind = match kind.try_into() {
|
||||
Ok(kind) => kind,
|
||||
Err(int) => {
|
||||
eprintln!("unrecognized packet kind {}", int);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(message) = self.on_packet(from, kind, buf) {
|
||||
tui::add_message(&mut siv_runner, &message);
|
||||
siv_runner.refresh(); // TODO better refresh management
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.on_packet(from, kind, buf);
|
||||
if let Some(other) = self.other.as_ref() {
|
||||
while let Ok(message) = self.message_receiver.try_recv() {
|
||||
eprintln!("sending message: {}", message);
|
||||
self.send_packet(other, PacketKind::Message, |writer| {
|
||||
let message = Message {
|
||||
sender: self.args.username.clone(),
|
||||
contents: message,
|
||||
};
|
||||
message.encode(writer)
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_packet(&mut self, from: SocketAddr, kind: PacketKind, mut reader: &[u8]) {
|
||||
pub fn on_packet(&mut self, from: SocketAddr, kind: PacketKind, mut reader: &[u8]) -> Option<Message> {
|
||||
println!("handling {:?}", kind);
|
||||
|
||||
// TODO proper connection management
|
||||
self.other = Some(from);
|
||||
|
||||
match kind {
|
||||
PacketKind::Ping => self.send_empty_packet(from, PacketKind::Pong).unwrap(),
|
||||
PacketKind::Pong => self
|
||||
|
@ -169,8 +212,14 @@ impl App {
|
|||
eprintln!("Received room info: {:#?}", info);
|
||||
self.remote_rooms.insert(info.id.clone(), Room { info });
|
||||
}
|
||||
PacketKind::Message => {
|
||||
let message = Message::decode(&mut reader).unwrap();
|
||||
return Some(message);
|
||||
}
|
||||
kind => eprintln!("unimplemented packet handler for {:?}", kind),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn build_room_list(&self) -> RoomList {
|
||||
|
|
|
@ -5,7 +5,8 @@ pub const EXAMPLE_USAGE: &str = "{S} went to the park.
|
|||
I went with {o}.
|
||||
{S} brought {p} frisbee.
|
||||
At least I think it was {pp}.
|
||||
{S} threw the frisbee to {r}.";
|
||||
{S} threw the frisbee to {r}.
|
||||
{S} {{if pl}}are{{else}}is{{endif}} happy.";
|
||||
|
||||
pub fn make_presets() -> Vec<Pronouns> {
|
||||
// TODO add more from https://pronoun.is and https://askanonbinary.tumblr.com/pronouns
|
||||
|
@ -13,6 +14,7 @@ pub fn make_presets() -> Vec<Pronouns> {
|
|||
(false, false, "she", "her", "her", "hers", "herself"),
|
||||
(false, false, "he", "him", "his", "his", "himself"),
|
||||
(false, true, "they", "them", "their", "theirs", "themselves"),
|
||||
(false, true, "they", "them", "their", "theirs", "themself"),
|
||||
(false, false, "fae", "faer", "faer", "faers", "faerself"),
|
||||
(false, false, "e", "em", "eir", "eirs", "emself"),
|
||||
(true, false, "E", "Em", "Eir", "Eirs", "Emself"),
|
||||
|
@ -146,7 +148,10 @@ impl Pronouns {
|
|||
/// Baked pronoun lookup table for formatting messages.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct PronounTable {
|
||||
#[serde(rename = "cs")]
|
||||
pub case_sensitive: bool,
|
||||
|
||||
#[serde(rename = "pl")]
|
||||
pub plural: bool,
|
||||
|
||||
#[serde(rename = "s")]
|
||||
|
|
591
src/tui.rs
591
src/tui.rs
|
@ -1,338 +1,307 @@
|
|||
use crate::pronouns::Pronouns;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use crossbeam_channel::Sender;
|
||||
use cursive::align::*;
|
||||
use cursive::event::{Event, Key};
|
||||
use cursive::theme::*;
|
||||
use cursive::traits::*;
|
||||
use cursive::view::*;
|
||||
use cursive::views::*;
|
||||
use cursive::{Cursive, CursiveExt};
|
||||
use cursive::Cursive;
|
||||
|
||||
pub enum EditEvent {
|
||||
Name(String),
|
||||
About(String),
|
||||
Pronouns(Option<Pronouns>),
|
||||
pub fn make_cursive(message_sender: Sender<String>) -> Cursive {
|
||||
let mut cursive = Cursive::new();
|
||||
cursive.set_user_data(message_sender);
|
||||
|
||||
cursive.update_theme(|theme| {
|
||||
theme.shadow = false;
|
||||
theme.borders = BorderStyle::Simple;
|
||||
|
||||
let palette = &mut theme.palette;
|
||||
palette[PaletteColor::Background] = Color::TerminalDefault;
|
||||
palette[PaletteColor::View] = Color::TerminalDefault;
|
||||
palette[PaletteColor::Primary] = Color::TerminalDefault;
|
||||
});
|
||||
|
||||
cursive.add_global_callback(Event::Char('h'), |siv| siv.on_event(Event::Key(Key::Left)));
|
||||
cursive.add_global_callback(Event::Char('j'), |siv| siv.on_event(Event::Key(Key::Down)));
|
||||
cursive.add_global_callback(Event::Char('k'), |siv| siv.on_event(Event::Key(Key::Up)));
|
||||
cursive.add_global_callback(Event::Char('l'), |siv| siv.on_event(Event::Key(Key::Right)));
|
||||
|
||||
show_welcome_mat(&mut cursive);
|
||||
|
||||
cursive
|
||||
}
|
||||
|
||||
pub struct Tui {
|
||||
cursive: Cursive,
|
||||
edit_receiver: Receiver<EditEvent>,
|
||||
identity: Identity,
|
||||
}
|
||||
pub fn add_message(siv: &mut Cursive, message: &crate::Message) {
|
||||
siv.call_on_name("messages_list", |messages: &mut LinearLayout| {
|
||||
let text = format!("{:<16}{}", message.sender, message.contents);
|
||||
let mut text = TextView::new(text);
|
||||
text.set_content_wrap(true);
|
||||
messages.add_child(text);
|
||||
});
|
||||
|
||||
impl Tui {
|
||||
pub fn new() -> Self {
|
||||
let (edit_sender, edit_receiver) = crossbeam_channel::unbounded();
|
||||
let mut cursive = Cursive::new();
|
||||
cursive.set_user_data(edit_sender);
|
||||
|
||||
cursive.update_theme(|theme| {
|
||||
theme.shadow = false;
|
||||
theme.borders = BorderStyle::Simple;
|
||||
|
||||
let palette = &mut theme.palette;
|
||||
palette[PaletteColor::Background] = Color::TerminalDefault;
|
||||
palette[PaletteColor::View] = Color::TerminalDefault;
|
||||
palette[PaletteColor::Primary] = Color::TerminalDefault;
|
||||
});
|
||||
|
||||
cursive.add_global_callback(Event::Char('h'), |siv| siv.on_event(Event::Key(Key::Left)));
|
||||
cursive.add_global_callback(Event::Char('j'), |siv| siv.on_event(Event::Key(Key::Down)));
|
||||
cursive.add_global_callback(Event::Char('k'), |siv| siv.on_event(Event::Key(Key::Up)));
|
||||
cursive.add_global_callback(Event::Char('l'), |siv| siv.on_event(Event::Key(Key::Right)));
|
||||
|
||||
Self {
|
||||
cursive,
|
||||
edit_receiver,
|
||||
identity: Identity {
|
||||
name: "<display name>".into(),
|
||||
about: "<short about text>".into(),
|
||||
pronouns: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
/*Self::edit_identity(&mut self.cursive);
|
||||
self.cursive.run();
|
||||
|
||||
while let Ok(edit) = self.edit_receiver.try_recv() {
|
||||
match edit {
|
||||
EditEvent::Name(name) => self.identity.name = name,
|
||||
EditEvent::About(about) => self.identity.about = about,
|
||||
EditEvent::Pronouns(pronouns) => self.identity.pronouns = pronouns,
|
||||
siv.call_on_name(
|
||||
"messages_list_scroll",
|
||||
|scroll: &mut ScrollView<NamedView<LinearLayout>>| {
|
||||
if scroll.is_at_bottom() {
|
||||
scroll.set_scroll_strategy(ScrollStrategy::StickToBottom);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Self::show_main(&mut self.cursive);
|
||||
self.cursive.run();*/
|
||||
pub fn show_welcome_mat(siv: &mut Cursive) {
|
||||
let logo = TextView::new(include_str!("logo.txt")).center();
|
||||
let labels = TextView::new("Identity:");
|
||||
let values = Button::new_raw("<none>", |siv| edit_identity(siv));
|
||||
let config = LinearLayout::horizontal().child(labels).child(values);
|
||||
let layout = LinearLayout::vertical().child(logo).child(config);
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Welcome User!")
|
||||
.button("Link start!", |siv| show_main(siv))
|
||||
.button("Quit", Cursive::quit);
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
Self::show_welcome_mat(&mut self.cursive);
|
||||
self.cursive.run();
|
||||
}
|
||||
pub fn edit_identity(siv: &mut Cursive) {
|
||||
let labels = make_vertical_labels(&["Name:", "About:", "Pronouns:"]).fixed_width(10);
|
||||
|
||||
pub fn show_welcome_mat(siv: &mut Cursive) {
|
||||
let logo = TextView::new(include_str!("logo.txt")).center();
|
||||
let labels = TextView::new("Identity:");
|
||||
let values = Button::new_raw("<none>", |siv| Self::edit_identity(siv));
|
||||
let config = LinearLayout::horizontal().child(labels).child(values);
|
||||
let layout = LinearLayout::vertical().child(logo).child(config);
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Welcome User!")
|
||||
.button("Link start!", |siv| Self::show_main(siv))
|
||||
.button("Quit", Cursive::quit);
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
let values = LinearLayout::vertical()
|
||||
.child(EditView::new().with_name("name_edit"))
|
||||
.child(EditView::new().with_name("about_edit"))
|
||||
.child(TextView::new("<none>").with_name("pronouns_text"))
|
||||
.fixed_width(45);
|
||||
|
||||
pub fn edit_identity(siv: &mut Cursive) {
|
||||
let labels = make_vertical_labels(&["Name:", "About:", "Pronouns:"]).fixed_width(10);
|
||||
let columns = LinearLayout::horizontal().child(labels).child(values);
|
||||
let dialog = Dialog::around(columns)
|
||||
.title("Edit Identity")
|
||||
.button("Select Pronouns...", |siv| select_pronouns(siv))
|
||||
.dismiss_button("Ok");
|
||||
|
||||
let values = LinearLayout::vertical()
|
||||
.child(EditView::new().with_name("name_edit"))
|
||||
.child(EditView::new().with_name("about_edit"))
|
||||
.child(TextView::new("<none>").with_name("pronouns_text"))
|
||||
.fixed_width(45);
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
let columns = LinearLayout::horizontal().child(labels).child(values);
|
||||
let mut dialog = Dialog::around(columns);
|
||||
dialog.set_title("Edit Identity");
|
||||
dialog.add_button("Select Pronouns...", |siv| Self::select_pronouns(siv));
|
||||
dialog.add_button("Ok", |siv| {
|
||||
let name = get_edit_contents(siv, "name_edit");
|
||||
let about = get_edit_contents(siv, "about_edit");
|
||||
siv.with_user_data(|sender: &mut Sender<EditEvent>| {
|
||||
sender.send(EditEvent::Name(name)).unwrap();
|
||||
sender.send(EditEvent::About(about)).unwrap();
|
||||
});
|
||||
fn make_example_usage_panel() -> impl View {
|
||||
let text = TextView::new("Highlight a pronoun set to preview its usage!")
|
||||
.with_name("pronoun_example_text")
|
||||
.fixed_width(50)
|
||||
.scrollable();
|
||||
Panel::new(text).title("Example Usage")
|
||||
}
|
||||
|
||||
fn update_pronouns_edit(siv: &mut Cursive, pronouns: &Pronouns) {
|
||||
siv.call_on_name("pronoun_example_text", |view: &mut TextView| {
|
||||
view.set_content(pronouns.make_example_usage());
|
||||
});
|
||||
|
||||
siv.call_on_name("pronouns_text", |view: &mut TextView| {
|
||||
view.set_content(pronouns.format_full());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn select_pronouns(siv: &mut Cursive) {
|
||||
let example = TextView::new("Highlight a pronoun set to preview its usage!")
|
||||
.with_name("pronoun_example_text")
|
||||
.fixed_width(35)
|
||||
.scrollable();
|
||||
|
||||
let presets = SelectView::new()
|
||||
.with_all(
|
||||
crate::pronouns::make_presets()
|
||||
.into_iter()
|
||||
.map(|pronouns| (pronouns.format_full(), pronouns)),
|
||||
)
|
||||
.on_select(|siv, pronouns| update_pronouns_edit(siv, pronouns))
|
||||
.scrollable();
|
||||
|
||||
let layout = LinearLayout::horizontal()
|
||||
.child(Panel::new(presets).title("Presets"))
|
||||
.child(Panel::new(example).title("Example Usage"));
|
||||
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Select Pronouns")
|
||||
.button("Custom...", |siv| {
|
||||
siv.pop_layer();
|
||||
edit_pronouns(siv);
|
||||
})
|
||||
.dismiss_button("Ok")
|
||||
.button("None", |siv| {
|
||||
siv.call_on_name("pronouns_text", |view: &mut TextView| {
|
||||
view.set_content("<none>");
|
||||
})
|
||||
.unwrap();
|
||||
siv.pop_layer();
|
||||
});
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
fn get_edit_pronouns(siv: &mut Cursive) -> Pronouns {
|
||||
let case_sensitive = get_checkbox_contents(siv, "case_sensitive_edit");
|
||||
let plural = get_checkbox_contents(siv, "plural_edit");
|
||||
let subject = get_edit_contents(siv, "subject_edit");
|
||||
let object = get_edit_contents(siv, "object_edit");
|
||||
let possessive = get_edit_contents(siv, "possessive_edit");
|
||||
let possessive_pronoun = get_edit_contents(siv, "possessive_pronoun_edit");
|
||||
let reflexive = get_edit_contents(siv, "reflexive_edit");
|
||||
|
||||
fn make_example_usage_panel() -> impl View {
|
||||
let text = TextView::new("Highlight a pronoun set to preview its usage!")
|
||||
.with_name("pronoun_example_text")
|
||||
.fixed_width(50)
|
||||
.scrollable();
|
||||
Panel::new(text).title("Example Usage")
|
||||
}
|
||||
|
||||
fn update_pronouns_edit(siv: &mut Cursive, pronouns: &Pronouns) {
|
||||
siv.call_on_name("pronoun_example_text", |view: &mut TextView| {
|
||||
view.set_content(pronouns.make_example_usage());
|
||||
});
|
||||
|
||||
siv.with_user_data(|sender: &mut Sender<EditEvent>| {
|
||||
sender
|
||||
.send(EditEvent::Pronouns(Some(pronouns.clone())))
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
siv.call_on_name("pronouns_text", |view: &mut TextView| {
|
||||
view.set_content(pronouns.format_full());
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn select_pronouns(siv: &mut Cursive) {
|
||||
let example = TextView::new("Highlight a pronoun set to preview its usage!")
|
||||
.with_name("pronoun_example_text")
|
||||
.fixed_width(35)
|
||||
.scrollable();
|
||||
|
||||
let presets = SelectView::new()
|
||||
.with_all(
|
||||
crate::pronouns::make_presets()
|
||||
.into_iter()
|
||||
.map(|pronouns| (pronouns.format_full(), pronouns)),
|
||||
)
|
||||
.on_select(|siv, pronouns| Self::update_pronouns_edit(siv, pronouns))
|
||||
.scrollable();
|
||||
|
||||
let layout = LinearLayout::horizontal()
|
||||
.child(Panel::new(presets).title("Presets"))
|
||||
.child(Panel::new(example).title("Example Usage"));
|
||||
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Select Pronouns")
|
||||
.button("Custom...", |siv| {
|
||||
siv.pop_layer();
|
||||
Self::edit_pronouns(siv);
|
||||
})
|
||||
.dismiss_button("Ok")
|
||||
.button("None", |siv| {
|
||||
siv.with_user_data(|sender: &mut Sender<EditEvent>| {
|
||||
sender.send(EditEvent::Pronouns(None)).unwrap();
|
||||
});
|
||||
siv.call_on_name("pronouns_text", |view: &mut TextView| {
|
||||
view.set_content("<none>");
|
||||
})
|
||||
.unwrap();
|
||||
siv.pop_layer();
|
||||
});
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
fn get_edit_pronouns(siv: &mut Cursive) -> Pronouns {
|
||||
let case_sensitive = get_checkbox_contents(siv, "case_sensitive_edit");
|
||||
let plural = get_checkbox_contents(siv, "plural_edit");
|
||||
let subject = get_edit_contents(siv, "subject_edit");
|
||||
let object = get_edit_contents(siv, "object_edit");
|
||||
let possessive = get_edit_contents(siv, "possessive_edit");
|
||||
let possessive_pronoun = get_edit_contents(siv, "possessive_pronoun_edit");
|
||||
let reflexive = get_edit_contents(siv, "reflexive_edit");
|
||||
|
||||
Pronouns {
|
||||
case_sensitive,
|
||||
plural,
|
||||
subject,
|
||||
object,
|
||||
possessive,
|
||||
possessive_pronoun,
|
||||
reflexive,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit_pronouns(siv: &mut Cursive) {
|
||||
let labels = make_vertical_labels(&[
|
||||
"Case-sensitive:",
|
||||
"Plural:",
|
||||
"Subject:",
|
||||
"Object:",
|
||||
"Possessive:",
|
||||
"Possessive pronoun:",
|
||||
"Reflexive:",
|
||||
])
|
||||
.fixed_width(20);
|
||||
|
||||
let mut values = LinearLayout::vertical();
|
||||
|
||||
let checkboxes = &["case_sensitive_edit", "plural_edit"];
|
||||
|
||||
for name in checkboxes.iter() {
|
||||
values.add_child(
|
||||
Checkbox::new()
|
||||
.on_change(|siv, _value| {
|
||||
let pronouns = Self::get_edit_pronouns(siv);
|
||||
Self::update_pronouns_edit(siv, &pronouns);
|
||||
})
|
||||
.with_name(*name),
|
||||
);
|
||||
}
|
||||
|
||||
let edit_views = &[
|
||||
"subject_edit",
|
||||
"object_edit",
|
||||
"possessive_edit",
|
||||
"possessive_pronoun_edit",
|
||||
"reflexive_edit",
|
||||
];
|
||||
|
||||
for name in edit_views.iter() {
|
||||
values.add_child(
|
||||
EditView::new()
|
||||
.on_edit(|siv, _text, _cursor| {
|
||||
let pronouns = Self::get_edit_pronouns(siv);
|
||||
Self::update_pronouns_edit(siv, &pronouns);
|
||||
})
|
||||
.with_name(*name),
|
||||
);
|
||||
}
|
||||
|
||||
let edit_layout = Panel::new(
|
||||
LinearLayout::horizontal()
|
||||
.child(labels)
|
||||
.child(values.fixed_width(12)),
|
||||
);
|
||||
|
||||
let example = Self::make_example_usage_panel();
|
||||
|
||||
let layout = LinearLayout::horizontal().child(edit_layout).child(example);
|
||||
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Edit Pronouns")
|
||||
.button("Ok", |siv| {
|
||||
let pronouns = Self::get_edit_pronouns(siv);
|
||||
Self::update_pronouns_edit(siv, &pronouns);
|
||||
siv.pop_layer();
|
||||
})
|
||||
.dismiss_button("Cancel");
|
||||
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
pub fn show_main(siv: &mut Cursive) {
|
||||
let messages = LinearLayout::vertical()
|
||||
.child(TextView::new("Hello, world!"))
|
||||
.with_name("messages_list")
|
||||
.scrollable()
|
||||
.full_height();
|
||||
|
||||
let message_edit = EditView::new()
|
||||
.on_submit(|siv, text| {
|
||||
siv.call_on_name("message_edit", |message: &mut EditView| {
|
||||
message.set_content("");
|
||||
});
|
||||
siv.call_on_name("messages_list", |messages: &mut LinearLayout| {
|
||||
messages.add_child(TextView::new(text));
|
||||
});
|
||||
})
|
||||
.with_name("message_edit")
|
||||
.full_width();
|
||||
|
||||
let message_edit = Panel::new(message_edit)
|
||||
.title("Send Message")
|
||||
.title_position(HAlign::Left);
|
||||
|
||||
let upload_button = Button::new("Upload", |siv| {
|
||||
siv.add_layer(Dialog::info("Uploading system goes here"))
|
||||
});
|
||||
|
||||
let message_buttons = Panel::new(upload_button);
|
||||
|
||||
let message_bar = LinearLayout::horizontal()
|
||||
.child(message_edit)
|
||||
.child(message_buttons);
|
||||
|
||||
let chat = LinearLayout::vertical().child(messages).child(message_bar);
|
||||
let chat = Panel::new(chat).title("Chat");
|
||||
|
||||
let mut rooms = SelectView::<&'static str>::new().on_submit(|siv, room: &str| {
|
||||
let dialog = Dialog::info(format!("Selected room: {}", room));
|
||||
siv.add_layer(dialog);
|
||||
});
|
||||
|
||||
rooms.add_item("Room 1", "room_id_1");
|
||||
rooms.add_item("Room 2", "room_id_2");
|
||||
rooms.add_item("Room 3", "room_id_3");
|
||||
|
||||
let rooms = Dialog::around(rooms)
|
||||
.button("Create...", |siv| {
|
||||
siv.add_layer(Dialog::info("Room creation dialog goes here"))
|
||||
})
|
||||
.title("Rooms")
|
||||
.title_position(HAlign::Left)
|
||||
.with_name("room_select");
|
||||
|
||||
let mut connections = SelectView::new();
|
||||
connections.add_item("Connection 1", "connection_1");
|
||||
let connections = Dialog::around(connections)
|
||||
.title("Connections")
|
||||
.title_position(HAlign::Left)
|
||||
.with_name("connection_s");
|
||||
|
||||
let sidebar = LinearLayout::vertical().child(rooms).child(connections);
|
||||
|
||||
let layout = LinearLayout::horizontal().child(sidebar).child(chat);
|
||||
|
||||
siv.add_fullscreen_layer(layout);
|
||||
Pronouns {
|
||||
case_sensitive,
|
||||
plural,
|
||||
subject,
|
||||
object,
|
||||
possessive,
|
||||
possessive_pronoun,
|
||||
reflexive,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Identity {
|
||||
pub name: String,
|
||||
pub about: String,
|
||||
pub pronouns: Option<Pronouns>,
|
||||
pub fn edit_pronouns(siv: &mut Cursive) {
|
||||
let labels = make_vertical_labels(&[
|
||||
"Case-sensitive:",
|
||||
"Plural:",
|
||||
"Subject:",
|
||||
"Object:",
|
||||
"Possessive:",
|
||||
"Possessive pronoun:",
|
||||
"Reflexive:",
|
||||
])
|
||||
.fixed_width(20);
|
||||
|
||||
let mut values = LinearLayout::vertical();
|
||||
|
||||
let checkboxes = &["case_sensitive_edit", "plural_edit"];
|
||||
|
||||
for name in checkboxes.iter() {
|
||||
values.add_child(
|
||||
Checkbox::new()
|
||||
.on_change(|siv, _value| {
|
||||
let pronouns = get_edit_pronouns(siv);
|
||||
update_pronouns_edit(siv, &pronouns);
|
||||
})
|
||||
.with_name(*name),
|
||||
);
|
||||
}
|
||||
|
||||
let edit_views = &[
|
||||
"subject_edit",
|
||||
"object_edit",
|
||||
"possessive_edit",
|
||||
"possessive_pronoun_edit",
|
||||
"reflexive_edit",
|
||||
];
|
||||
|
||||
for name in edit_views.iter() {
|
||||
values.add_child(
|
||||
EditView::new()
|
||||
.on_edit(|siv, _text, _cursor| {
|
||||
let pronouns = get_edit_pronouns(siv);
|
||||
update_pronouns_edit(siv, &pronouns);
|
||||
})
|
||||
.with_name(*name),
|
||||
);
|
||||
}
|
||||
|
||||
let edit_layout = Panel::new(
|
||||
LinearLayout::horizontal()
|
||||
.child(labels)
|
||||
.child(values.fixed_width(12)),
|
||||
);
|
||||
|
||||
let example = make_example_usage_panel();
|
||||
|
||||
let layout = LinearLayout::horizontal().child(edit_layout).child(example);
|
||||
|
||||
let dialog = Dialog::around(layout)
|
||||
.title("Edit Pronouns")
|
||||
.button("Ok", |siv| {
|
||||
let pronouns = get_edit_pronouns(siv);
|
||||
update_pronouns_edit(siv, &pronouns);
|
||||
siv.pop_layer();
|
||||
})
|
||||
.dismiss_button("Cancel");
|
||||
|
||||
siv.add_layer(dialog);
|
||||
}
|
||||
|
||||
pub fn show_main(siv: &mut Cursive) {
|
||||
let messages = LinearLayout::vertical()
|
||||
.child(TextView::new("Hello, world!"))
|
||||
.with_name("messages_list");
|
||||
|
||||
let messages = ScrollView::new(messages)
|
||||
.scroll_strategy(ScrollStrategy::StickToBottom)
|
||||
.with_name("messages_list_scroll")
|
||||
.full_height();
|
||||
|
||||
let message_edit = EditView::new()
|
||||
.on_submit(|siv, text| {
|
||||
siv.call_on_name("message_edit", |message: &mut EditView| {
|
||||
message.set_content("");
|
||||
});
|
||||
siv.with_user_data(|message_sender: &mut Sender<String>| {
|
||||
message_sender.send(text.to_string()).unwrap();
|
||||
});
|
||||
add_message(
|
||||
siv,
|
||||
&crate::Message {
|
||||
sender: "me".to_string(),
|
||||
contents: text.to_owned(),
|
||||
},
|
||||
);
|
||||
})
|
||||
.with_name("message_edit")
|
||||
.full_width();
|
||||
|
||||
let message_edit = Panel::new(message_edit)
|
||||
.title("Send Message")
|
||||
.title_position(HAlign::Left);
|
||||
|
||||
let upload_button = Button::new("Upload", |siv| {
|
||||
siv.add_layer(Dialog::info("Uploading system goes here"))
|
||||
});
|
||||
|
||||
let message_buttons = Panel::new(upload_button);
|
||||
|
||||
let message_bar = LinearLayout::horizontal()
|
||||
.child(message_edit)
|
||||
.child(message_buttons);
|
||||
|
||||
let chat = LinearLayout::vertical().child(messages).child(message_bar);
|
||||
let chat = Panel::new(chat).title("Chat");
|
||||
|
||||
let mut rooms = SelectView::<&'static str>::new().on_submit(|siv, room: &str| {
|
||||
let dialog = Dialog::info(format!("Selected room: {}", room));
|
||||
siv.add_layer(dialog);
|
||||
});
|
||||
|
||||
rooms.add_item("Room 1", "room_id_1");
|
||||
rooms.add_item("Room 2", "room_id_2");
|
||||
rooms.add_item("Room 3", "room_id_3");
|
||||
|
||||
let rooms = Dialog::around(rooms)
|
||||
.button("Create...", |siv| {
|
||||
siv.add_layer(Dialog::info("Room creation dialog goes here"))
|
||||
})
|
||||
.title("Rooms")
|
||||
.title_position(HAlign::Left)
|
||||
.with_name("room_select");
|
||||
|
||||
let mut connections = SelectView::new();
|
||||
connections.add_item("Connection 1", "connection_1");
|
||||
let connections = Dialog::around(connections)
|
||||
.title("Connections")
|
||||
.title_position(HAlign::Left)
|
||||
.with_name("connection_s");
|
||||
|
||||
let sidebar = LinearLayout::vertical()
|
||||
.child(rooms)
|
||||
.child(connections)
|
||||
.fixed_width(24);
|
||||
|
||||
let layout = LinearLayout::horizontal().child(sidebar).child(chat);
|
||||
|
||||
siv.add_fullscreen_layer(layout);
|
||||
}
|
||||
|
||||
fn get_edit_contents(siv: &mut Cursive, name: &str) -> String {
|
||||
|
|
Loading…
Reference in New Issue