update `cursive`

`cursive` now requires `Send + Sync` bounds, so I had to switch from
`Rc<RefCell<...>>` to `Arc<Mutex<...>>`. I also coalesced some
lock calls together, hopefully without introducing any deadlocks.
I don't see any calls into the UI that would need the model while these
are held, and it seemed fine in a quick test.
This commit is contained in:
Scott Lamb 2024-08-24 13:11:17 -07:00
parent f9e3fb56b3
commit 06f942582c
5 changed files with 174 additions and 81 deletions

137
server/Cargo.lock generated
View File

@ -200,6 +200,15 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50"
[[package]]
name = "castaway"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5"
dependencies = [
"rustversion",
]
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.1.14" version = "1.1.14"
@ -244,6 +253,20 @@ name = "coded"
version = "0.2.0-pre" version = "0.2.0-pre"
source = "git+https://github.com/scottlamb/coded?rev=2c97994974a73243d5dd12134831814f42cdb0e8#2c97994974a73243d5dd12134831814f42cdb0e8" source = "git+https://github.com/scottlamb/coded?rev=2c97994974a73243d5dd12134831814f42cdb0e8#2c97994974a73243d5dd12134831814f42cdb0e8"
[[package]]
name = "compact_str"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644"
dependencies = [
"castaway",
"cfg-if",
"itoa",
"rustversion",
"ryu",
"static_assertions",
]
[[package]] [[package]]
name = "constant_time_eq" name = "constant_time_eq"
version = "0.3.0" version = "0.3.0"
@ -330,9 +353,9 @@ dependencies = [
[[package]] [[package]]
name = "cursive" name = "cursive"
version = "0.20.0" version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5438eb16bdd8af51b31e74764fef5d0a9260227a5ec82ba75c9d11ce46595839" checksum = "386d5a36020bb856e9a34ecb8a4e6c9bd6b0262d1857bae4db7bc7e2fdaa532e"
dependencies = [ dependencies = [
"ahash", "ahash",
"cfg-if", "cfg-if",
@ -348,19 +371,31 @@ dependencies = [
] ]
[[package]] [[package]]
name = "cursive_core" name = "cursive-macros"
version = "0.3.7" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4db3b58161228d0dcb45c7968c5e74c3f03ad39e8983e58ad7d57061aa2cd94d" checksum = "ac7ac0eb0cede3dfdfebf4d5f22354e05a730b79c25fd03481fc69fcfba0a73e"
dependencies = [
"proc-macro2",
]
[[package]]
name = "cursive_core"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "321ec774d27fafc66e812034d0025f8858bd7d9095304ff8fc200e0b9f9cc257"
dependencies = [ dependencies = [
"ahash", "ahash",
"compact_str",
"crossbeam-channel", "crossbeam-channel",
"cursive-macros",
"enum-map", "enum-map",
"enumset", "enumset",
"lazy_static", "lazy_static",
"log", "log",
"num", "num",
"owning_ref", "parking_lot",
"serde_json",
"time 0.3.36", "time 0.3.36",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
@ -980,6 +1015,17 @@ version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libredox"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3af92c55d7d839293953fcd0fda5ecfe93297cfde6ffbdec13b41d99c0ba6607"
dependencies = [
"bitflags 2.6.0",
"libc",
"redox_syscall 0.4.1",
]
[[package]] [[package]]
name = "libsqlite3-sys" name = "libsqlite3-sys"
version = "0.30.1" version = "0.30.1"
@ -1015,6 +1061,16 @@ version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.22"
@ -1399,21 +1455,35 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "owning_ref"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce"
dependencies = [
"stable_deref_trait",
]
[[package]] [[package]]
name = "owo-colors" name = "owo-colors"
version = "4.0.0" version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.5.3",
"smallvec",
"windows-targets 0.52.6",
]
[[package]] [[package]]
name = "password-hash" name = "password-hash"
version = "0.5.0" version = "0.5.0"
@ -1616,13 +1686,22 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.16" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [ dependencies = [
"bitflags 1.3.2", "bitflags 1.3.2",
] ]
[[package]]
name = "redox_syscall"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4"
dependencies = [
"bitflags 2.6.0",
]
[[package]] [[package]]
name = "redox_termios" name = "redox_termios"
version = "0.1.3" version = "0.1.3"
@ -1802,6 +1881,12 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.18" version = "1.0.18"
@ -1826,6 +1911,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "scrypt" name = "scrypt"
version = "0.11.0" version = "0.11.0"
@ -1994,6 +2085,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]] [[package]]
name = "subtle" name = "subtle"
version = "2.6.1" version = "2.6.1"
@ -2062,13 +2159,13 @@ dependencies = [
[[package]] [[package]]
name = "termion" name = "termion"
version = "1.5.6" version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" checksum = "1ccce68e518d1173e80876edd54760b60b792750d0cab6444a79101c6ea03848"
dependencies = [ dependencies = [
"libc", "libc",
"libredox",
"numtoa", "numtoa",
"redox_syscall",
"redox_termios", "redox_termios",
] ]

View File

@ -43,7 +43,7 @@ blake3 = "1.0.0"
bpaf = { version = "0.9.1", features = ["autocomplete", "bright-color", "derive"]} bpaf = { version = "0.9.1", features = ["autocomplete", "bright-color", "derive"]}
bytes = "1" bytes = "1"
byteorder = "1.0" byteorder = "1.0"
cursive = { version = "0.20.0", default-features = false, features = ["termion-backend"] } cursive = { version = "0.21.1", default-features = false, features = ["termion-backend"] }
db = { package = "moonfire-db", path = "db" } db = { package = "moonfire-db", path = "db" }
futures = "0.3" futures = "0.3"
h264-reader = { workspace = true } h264-reader = { workspace = true }

View File

@ -9,11 +9,9 @@ use cursive::view::Scrollable;
use cursive::Cursive; use cursive::Cursive;
use cursive::{views, With}; use cursive::{views, With};
use db::writer; use db::writer;
use std::cell::RefCell;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::path::Path; use std::path::Path;
use std::rc::Rc; use std::sync::{Arc, Mutex};
use std::sync::Arc;
use tracing::{debug, trace}; use tracing::{debug, trace};
use super::tab_complete::TabCompleteEditView; use super::tab_complete::TabCompleteEditView;
@ -58,10 +56,8 @@ fn update_limits(model: &Model, siv: &mut Cursive) {
} }
} }
fn edit_limit(model: &RefCell<Model>, siv: &mut Cursive, id: i32, content: &str) { fn edit_limit(model: &mut Model, siv: &mut Cursive, id: i32, content: &str) {
debug!("on_edit called for id {}", id); debug!("on_edit called for id {}", id);
let mut model = model.borrow_mut();
let model: &mut Model = &mut model;
let stream = model.streams.get_mut(&id).unwrap(); let stream = model.streams.get_mut(&id).unwrap();
let new_value = decode_size(content).ok(); let new_value = decode_size(content).ok();
let delta = new_value.unwrap_or(0) - stream.retain.unwrap_or(0); let delta = new_value.unwrap_or(0) - stream.retain.unwrap_or(0);
@ -96,14 +92,12 @@ fn edit_limit(model: &RefCell<Model>, siv: &mut Cursive, id: i32, content: &str)
} }
} }
fn edit_record(model: &RefCell<Model>, id: i32, record: bool) { fn edit_record(model: &mut Model, id: i32, record: bool) {
let mut model = model.borrow_mut();
let model: &mut Model = &mut model;
let stream = model.streams.get_mut(&id).unwrap(); let stream = model.streams.get_mut(&id).unwrap();
stream.record = record; stream.record = record;
} }
fn confirm_deletion(model: &RefCell<Model>, siv: &mut Cursive, to_delete: i64) { fn confirm_deletion(model: &Mutex<Model>, siv: &mut Cursive, to_delete: i64) {
let typed = siv let typed = siv
.find_name::<views::EditView>("confirm") .find_name::<views::EditView>("confirm")
.unwrap() .unwrap()
@ -124,8 +118,8 @@ fn confirm_deletion(model: &RefCell<Model>, siv: &mut Cursive, to_delete: i64) {
} }
} }
fn actually_delete(model: &RefCell<Model>, siv: &mut Cursive) { fn actually_delete(model: &Mutex<Model>, siv: &mut Cursive) {
let model = &*model.borrow(); let model = model.lock().unwrap();
let new_limits: Vec<_> = model let new_limits: Vec<_> = model
.streams .streams
.iter() .iter()
@ -147,20 +141,21 @@ fn actually_delete(model: &RefCell<Model>, siv: &mut Cursive) {
.dismiss_button("Abort"), .dismiss_button("Abort"),
); );
} else { } else {
update_limits(model, siv); update_limits(&model, siv);
} }
} }
fn press_change(model: &Rc<RefCell<Model>>, siv: &mut Cursive) { fn press_change(model: &Arc<Mutex<Model>>, siv: &mut Cursive) {
if model.borrow().errors > 0 { let to_delete = {
return; let l = model.lock().unwrap();
} if l.errors > 0 {
let to_delete = model return;
.borrow() }
.streams l.streams
.values() .values()
.map(|s| ::std::cmp::max(s.used - s.retain.unwrap(), 0)) .map(|s| ::std::cmp::max(s.used - s.retain.unwrap(), 0))
.sum(); .sum()
};
debug!("change press, to_delete={}", to_delete); debug!("change press, to_delete={}", to_delete);
if to_delete > 0 { if to_delete > 0 {
let prompt = format!( let prompt = format!(
@ -190,7 +185,7 @@ fn press_change(model: &Rc<RefCell<Model>>, siv: &mut Cursive) {
siv.add_layer(dialog); siv.add_layer(dialog);
} else { } else {
siv.pop_layer(); siv.pop_layer();
update_limits(&model.borrow(), siv); update_limits(&model.lock().unwrap(), siv);
} }
} }
@ -367,7 +362,7 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
fs_capacity = stat.block_size() as i64 * stat.blocks_available() as i64 + total_used; fs_capacity = stat.block_size() as i64 * stat.blocks_available() as i64 + total_used;
path = dir.path.clone(); path = dir.path.clone();
} }
Rc::new(RefCell::new(Model { Arc::new(Mutex::new(Model {
dir_id, dir_id,
db: db.clone(), db: db.clone(),
fs_capacity, fs_capacity,
@ -389,12 +384,13 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
.child(views::TextView::new("usage").fixed_width(BYTES_WIDTH)) .child(views::TextView::new("usage").fixed_width(BYTES_WIDTH))
.child(views::TextView::new("limit").fixed_width(BYTES_WIDTH)), .child(views::TextView::new("limit").fixed_width(BYTES_WIDTH)),
); );
for (&id, stream) in &model.borrow().streams { let l = model.lock().unwrap();
for (&id, stream) in &l.streams {
let mut record_cb = views::Checkbox::new(); let mut record_cb = views::Checkbox::new();
record_cb.set_checked(stream.record); record_cb.set_checked(stream.record);
record_cb.set_on_change({ record_cb.set_on_change({
let model = model.clone(); let model = model.clone();
move |_siv, record| edit_record(&model, id, record) move |_siv, record| edit_record(&mut model.lock().unwrap(), id, record)
}); });
list.add_child( list.add_child(
&stream.label, &stream.label,
@ -406,7 +402,9 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
.content(encode_size(stream.retain.unwrap())) .content(encode_size(stream.retain.unwrap()))
.on_edit({ .on_edit({
let model = model.clone(); let model = model.clone();
move |siv, content, _pos| edit_limit(&model, siv, id, content) move |siv, content, _pos| {
edit_limit(&mut model.lock().unwrap(), siv, id, content)
}
}) })
.on_submit({ .on_submit({
let model = model.clone(); let model = model.clone();
@ -421,17 +419,14 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
), ),
); );
} }
let over = model.borrow().total_retain > model.borrow().fs_capacity; let over = l.total_retain > l.fs_capacity;
list.add_child( list.add_child(
"total", "total",
views::LinearLayout::horizontal() views::LinearLayout::horizontal()
.child(views::DummyView {}.fixed_width(RECORD_WIDTH)) .child(views::DummyView {}.fixed_width(RECORD_WIDTH))
.child(views::TextView::new(encode_size(l.total_used)).fixed_width(BYTES_WIDTH))
.child( .child(
views::TextView::new(encode_size(model.borrow().total_used)) views::TextView::new(encode_size(l.total_retain))
.fixed_width(BYTES_WIDTH),
)
.child(
views::TextView::new(encode_size(model.borrow().total_retain))
.with_name("total_retain") .with_name("total_retain")
.fixed_width(BYTES_WIDTH), .fixed_width(BYTES_WIDTH),
) )
@ -442,11 +437,9 @@ fn edit_dir_dialog(db: &Arc<db::Database>, siv: &mut Cursive, dir_id: i32) {
views::LinearLayout::horizontal() views::LinearLayout::horizontal()
.child(views::DummyView {}.fixed_width(RECORD_WIDTH)) .child(views::DummyView {}.fixed_width(RECORD_WIDTH))
.child(views::DummyView {}.fixed_width(BYTES_WIDTH)) .child(views::DummyView {}.fixed_width(BYTES_WIDTH))
.child( .child(views::TextView::new(encode_size(l.fs_capacity)).fixed_width(BYTES_WIDTH)),
views::TextView::new(encode_size(model.borrow().fs_capacity))
.fixed_width(BYTES_WIDTH),
),
); );
drop(l);
let mut change_button = views::Button::new("Change", move |siv| press_change(&model, siv)); let mut change_button = views::Button::new("Change", move |siv| press_change(&model, siv));
change_button.set_enabled(!over); change_button.set_enabled(!over);
let mut buttons = views::LinearLayout::horizontal().child(views::DummyView.full_width()); let mut buttons = views::LinearLayout::horizontal().child(views::DummyView.full_width());

View File

@ -2,7 +2,7 @@
// Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt. // Copyright (C) 2020 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt.
// SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception. // SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception.
use std::{cell::RefCell, rc::Rc}; use std::sync::{Arc, Mutex};
use cursive::{ use cursive::{
direction::Direction, direction::Direction,
@ -13,46 +13,49 @@ use cursive::{
Printer, Rect, Vec2, View, With, Printer, Rect, Vec2, View, With,
}; };
type TabCompleteFn = Rc<dyn Fn(&str) -> Vec<String>>; type TabCompleteFn = Arc<dyn Fn(&str) -> Vec<String> + Send + Sync>;
pub struct TabCompleteEditView { pub struct TabCompleteEditView {
edit_view: Rc<RefCell<EditView>>, edit_view: Arc<Mutex<EditView>>,
tab_completer: Option<TabCompleteFn>, tab_completer: Option<TabCompleteFn>,
} }
impl TabCompleteEditView { impl TabCompleteEditView {
pub fn new(edit_view: EditView) -> Self { pub fn new(edit_view: EditView) -> Self {
Self { Self {
edit_view: Rc::new(RefCell::new(edit_view)), edit_view: Arc::new(Mutex::new(edit_view)),
tab_completer: None, tab_completer: None,
} }
} }
pub fn on_tab_complete(mut self, handler: impl Fn(&str) -> Vec<String> + 'static) -> Self { pub fn on_tab_complete(
self.tab_completer = Some(Rc::new(handler)); mut self,
handler: impl Fn(&str) -> Vec<String> + Send + Sync + 'static,
) -> Self {
self.tab_completer = Some(Arc::new(handler));
self self
} }
pub fn get_content(&self) -> Rc<String> { pub fn get_content(&self) -> Arc<String> {
self.edit_view.borrow_mut().get_content() self.edit_view.lock().unwrap().get_content()
} }
} }
impl View for TabCompleteEditView { impl View for TabCompleteEditView {
fn draw(&self, printer: &Printer) { fn draw(&self, printer: &Printer) {
self.edit_view.borrow().draw(printer) self.edit_view.lock().unwrap().draw(printer)
} }
fn layout(&mut self, size: Vec2) { fn layout(&mut self, size: Vec2) {
self.edit_view.borrow_mut().layout(size) self.edit_view.lock().unwrap().layout(size)
} }
fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> { fn take_focus(&mut self, source: Direction) -> Result<EventResult, CannotFocus> {
self.edit_view.borrow_mut().take_focus(source) self.edit_view.lock().unwrap().take_focus(source)
} }
fn on_event(&mut self, event: Event) -> EventResult { fn on_event(&mut self, event: Event) -> EventResult {
if !self.edit_view.borrow().is_enabled() { if !self.edit_view.lock().unwrap().is_enabled() {
return EventResult::Ignored; return EventResult::Ignored;
} }
@ -63,32 +66,32 @@ impl View for TabCompleteEditView {
EventResult::consumed() EventResult::consumed()
} }
} else { } else {
self.edit_view.borrow_mut().on_event(event) self.edit_view.lock().unwrap().on_event(event)
} }
} }
fn important_area(&self, view_size: Vec2) -> Rect { fn important_area(&self, view_size: Vec2) -> Rect {
self.edit_view.borrow().important_area(view_size) self.edit_view.lock().unwrap().important_area(view_size)
} }
} }
fn tab_complete( fn tab_complete(
edit_view: Rc<RefCell<EditView>>, edit_view: Arc<Mutex<EditView>>,
tab_completer: TabCompleteFn, tab_completer: TabCompleteFn,
autofill_one: bool, autofill_one: bool,
) -> EventResult { ) -> EventResult {
let completions = tab_completer(edit_view.borrow().get_content().as_str()); let completions = tab_completer(edit_view.lock().unwrap().get_content().as_str());
EventResult::with_cb_once(move |siv| match *completions { EventResult::with_cb_once(move |siv| match *completions {
[] => {} [] => {}
[ref completion] if autofill_one => edit_view.borrow_mut().set_content(completion)(siv), [ref completion] if autofill_one => edit_view.lock().unwrap().set_content(completion)(siv),
[..] => { [..] => {
siv.add_layer(TabCompletePopup { siv.add_layer(TabCompletePopup {
popup: views::MenuPopup::new(Rc::new({ popup: views::MenuPopup::new(Arc::new({
menu::Tree::new().with(|tree| { menu::Tree::new().with(|tree| {
for completion in completions { for completion in completions {
let edit_view = edit_view.clone(); let edit_view = edit_view.clone();
tree.add_leaf(completion.clone(), move |siv| { tree.add_leaf(completion.clone(), move |siv| {
edit_view.borrow_mut().set_content(&completion)(siv) edit_view.lock().unwrap().set_content(&completion)(siv)
}) })
} }
}) })
@ -101,7 +104,7 @@ fn tab_complete(
} }
struct TabCompletePopup { struct TabCompletePopup {
edit_view: Rc<RefCell<EditView>>, edit_view: Arc<Mutex<EditView>>,
popup: MenuPopup, popup: MenuPopup,
tab_completer: TabCompleteFn, tab_completer: TabCompleteFn,
} }
@ -111,7 +114,7 @@ impl TabCompletePopup {
let tab_completer = self.tab_completer.clone(); let tab_completer = self.tab_completer.clone();
EventResult::with_cb_once(move |s| { EventResult::with_cb_once(move |s| {
s.pop_layer(); s.pop_layer();
edit_view.borrow_mut().on_event(event).process(s); edit_view.lock().unwrap().on_event(event).process(s);
tab_complete(edit_view, tab_completer, false).process(s); tab_complete(edit_view, tab_completer, false).process(s);
}) })
} }

View File

@ -189,7 +189,7 @@ fn edit_user_dialog(db: &Arc<db::Database>, siv: &mut Cursive, item: Option<i32>
] { ] {
let mut checkbox = views::Checkbox::new(); let mut checkbox = views::Checkbox::new();
checkbox.set_checked(*b); checkbox.set_checked(*b);
perms.add_child(name, checkbox.with_name(format!("perm_{name}"))); perms.add_child(*name, checkbox.with_name(format!("perm_{name}")));
} }
layout.add_child(perms); layout.add_child(perms);