2016-04-09 04:56:55 -04:00
|
|
|
use console;
|
|
|
|
use render;
|
|
|
|
use ui;
|
|
|
|
use settings;
|
|
|
|
|
2016-04-21 06:40:50 -04:00
|
|
|
use std::rc::Rc;
|
2016-04-09 04:56:55 -04:00
|
|
|
|
2016-04-21 06:00:05 -04:00
|
|
|
pub struct UIElements {
|
2016-04-24 07:22:04 -04:00
|
|
|
background: ui::ImageRef,
|
|
|
|
_buttons: Vec<ui::ButtonRef>,
|
2016-04-09 04:56:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct SettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
_vars: Rc<console::Vars>,
|
2016-04-21 06:00:05 -04:00
|
|
|
elements: Option<UIElements>,
|
2016-04-09 04:56:55 -04:00
|
|
|
show_disconnect_button: bool
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SettingsMenu {
|
2016-04-21 06:40:50 -04:00
|
|
|
pub fn new(vars: Rc<console::Vars>, show_disconnect_button: bool) -> SettingsMenu {
|
2016-04-09 04:56:55 -04:00
|
|
|
SettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
_vars: vars,
|
2016-04-09 04:56:55 -04:00
|
|
|
elements: None,
|
|
|
|
show_disconnect_button: show_disconnect_button
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl super::Screen for SettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
|
|
|
let background = ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, 0.0)
|
|
|
|
.size(854.0, 480.0)
|
|
|
|
.colour((0, 0, 0, 100))
|
|
|
|
.create(ui_container);
|
|
|
|
|
|
|
|
let mut buttons = vec![];
|
2016-04-21 06:00:05 -04:00
|
|
|
|
2016-04-09 04:56:55 -04:00
|
|
|
// From top and down
|
2016-04-24 07:22:04 -04:00
|
|
|
let audio_settings = ui::ButtonBuilder::new()
|
|
|
|
.position(-160.0, -50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut audio_settings = audio_settings.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Audio settings...")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *audio_settings);
|
|
|
|
audio_settings.add_text(txt);
|
|
|
|
audio_settings.add_click_func(|_, game| {
|
|
|
|
game.screen_sys.add_screen(Box::new(AudioSettingsMenu::new(game.vars.clone())));
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(audio_settings);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
2016-04-24 07:22:04 -04:00
|
|
|
let video_settings = ui::ButtonBuilder::new()
|
|
|
|
.position(160.0, -50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut video_settings = video_settings.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Video settings...")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *video_settings);
|
|
|
|
video_settings.add_text(txt);
|
|
|
|
video_settings.add_click_func(|_, game| {
|
|
|
|
game.screen_sys.add_screen(Box::new(VideoSettingsMenu::new(game.vars.clone())));
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(video_settings);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
2016-04-24 07:22:04 -04:00
|
|
|
let controls_settings = ui::ButtonBuilder::new()
|
|
|
|
.position(160.0, 0.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut controls_settings = controls_settings.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Controls...")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *controls_settings);
|
|
|
|
controls_settings.add_text(txt);
|
|
|
|
}
|
|
|
|
buttons.push(controls_settings);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
2016-04-24 07:22:04 -04:00
|
|
|
let lang_settings = ui::ButtonBuilder::new()
|
|
|
|
.position(-160.0, 0.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut lang_settings = lang_settings.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Language...")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *lang_settings);
|
|
|
|
lang_settings.add_text(txt);
|
|
|
|
}
|
|
|
|
buttons.push(lang_settings);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
// Center bottom items
|
2016-04-24 07:22:04 -04:00
|
|
|
let done_button = ui::ButtonBuilder::new()
|
|
|
|
.position(0.0, 50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut done_button = done_button.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Done")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *done_button);
|
|
|
|
done_button.add_text(txt);
|
|
|
|
done_button.add_click_func(|_, game| {
|
|
|
|
game.screen_sys.pop_screen();
|
|
|
|
game.focused = true;
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(done_button);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
if self.show_disconnect_button {
|
2016-04-24 07:22:04 -04:00
|
|
|
let disconnect_button = ui::ButtonBuilder::new()
|
|
|
|
.position(0.0, 100.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut disconnect_button = disconnect_button.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Disconnect")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *disconnect_button);
|
|
|
|
disconnect_button.add_text(txt);
|
|
|
|
disconnect_button.add_click_func(|_, game| {
|
|
|
|
game.server.disconnect(None);
|
|
|
|
game.screen_sys.replace_screen(Box::new(super::ServerList::new(None)));
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(disconnect_button);
|
2016-04-09 04:56:55 -04:00
|
|
|
}
|
|
|
|
|
2016-04-21 06:00:05 -04:00
|
|
|
self.elements = Some(UIElements {
|
|
|
|
background: background,
|
2016-04-24 07:22:04 -04:00
|
|
|
_buttons: buttons,
|
2016-04-09 04:56:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
|
2016-04-09 04:56:55 -04:00
|
|
|
self.elements = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called every frame the screen is active
|
2016-04-21 06:00:05 -04:00
|
|
|
fn tick(&mut self, _delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
|
|
|
|
let elements = self.elements.as_mut().unwrap();
|
|
|
|
{
|
|
|
|
let mode = ui_container.mode;
|
2016-04-24 07:22:04 -04:00
|
|
|
let mut background = elements.background.borrow_mut();
|
|
|
|
background.width = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 854.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.width as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
|
|
|
background.height = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 480.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.height as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
2016-04-21 06:00:05 -04:00
|
|
|
}
|
2016-04-09 04:56:55 -04:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events
|
2016-04-16 16:43:41 -04:00
|
|
|
fn on_scroll(&mut self, _x: f64, _y: f64) {
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_closable(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct VideoSettingsMenu {
|
2016-04-21 06:40:50 -04:00
|
|
|
vars: Rc<console::Vars>,
|
2016-04-21 06:00:05 -04:00
|
|
|
elements: Option<UIElements>,
|
2016-04-09 04:56:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl VideoSettingsMenu {
|
2016-04-21 06:40:50 -04:00
|
|
|
pub fn new(vars: Rc<console::Vars>) -> VideoSettingsMenu {
|
2016-04-09 04:56:55 -04:00
|
|
|
VideoSettingsMenu {
|
2016-04-21 06:40:50 -04:00
|
|
|
vars: vars,
|
2016-04-09 04:56:55 -04:00
|
|
|
elements: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl super::Screen for VideoSettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
|
|
|
let background = ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, 0.0)
|
|
|
|
.size(854.0, 480.0)
|
|
|
|
.colour((0, 0, 0, 100))
|
|
|
|
.create(ui_container);
|
|
|
|
|
|
|
|
let mut buttons = vec![];
|
2016-04-21 06:00:05 -04:00
|
|
|
|
2016-04-09 04:56:55 -04:00
|
|
|
// Load defaults
|
2016-04-21 06:40:50 -04:00
|
|
|
let r_max_fps = *self.vars.get(settings::R_MAX_FPS);
|
|
|
|
let r_fov = *self.vars.get(settings::R_FOV);
|
|
|
|
let r_vsync = *self.vars.get(settings::R_VSYNC);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
// Setting buttons
|
|
|
|
// TODO: Slider
|
2016-04-24 07:22:04 -04:00
|
|
|
let fov_setting = ui::ButtonBuilder::new()
|
|
|
|
.position(160.0, -50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut fov_setting = fov_setting.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text(format!("FOV: {}", match r_fov {
|
|
|
|
90 => "Normal".into(),
|
|
|
|
110 => "Quake pro".into(),
|
|
|
|
val => val.to_string(),
|
|
|
|
}))
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *fov_setting);
|
|
|
|
fov_setting.add_text(txt);
|
|
|
|
}
|
|
|
|
buttons.push(fov_setting);
|
|
|
|
|
|
|
|
let vsync_setting = ui::ButtonBuilder::new()
|
|
|
|
.position(-160.0, 0.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut vsync_setting = vsync_setting.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text(format!("VSync: {}", if r_vsync { "Enabled" } else { "Disabled" }))
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *vsync_setting);
|
|
|
|
let txt_vsync = txt.clone();
|
|
|
|
vsync_setting.add_text(txt);
|
|
|
|
vsync_setting.add_click_func(move |_, game| {
|
|
|
|
let r_vsync = !*game.vars.get(settings::R_VSYNC);
|
|
|
|
txt_vsync.borrow_mut().text = format!("VSync: {}", if r_vsync { "Enabled" } else { "Disabled" });
|
|
|
|
game.vars.set(settings::R_VSYNC, r_vsync);
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(vsync_setting);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
// TODO: Slider
|
2016-04-24 07:22:04 -04:00
|
|
|
let fps_setting = ui::ButtonBuilder::new()
|
|
|
|
.position(160.0, 0.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut fps_setting = fps_setting.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text(format!("FPS cap: {}", match r_max_fps {
|
|
|
|
0 => "Unlimited".into(),
|
|
|
|
val => val.to_string(),
|
|
|
|
}))
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *fps_setting);
|
|
|
|
fps_setting.add_text(txt);
|
|
|
|
}
|
|
|
|
buttons.push(fps_setting);
|
2016-04-09 04:56:55 -04:00
|
|
|
|
2016-04-24 07:22:04 -04:00
|
|
|
let done_button = ui::ButtonBuilder::new()
|
|
|
|
.position(0.0, 50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut done_button = done_button.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Done")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *done_button);
|
|
|
|
done_button.add_text(txt);
|
|
|
|
done_button.add_click_func(|_, game| {
|
|
|
|
game.screen_sys.pop_screen();
|
|
|
|
game.focused = true;
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(done_button);
|
2016-04-21 06:00:05 -04:00
|
|
|
self.elements = Some(UIElements {
|
|
|
|
background: background,
|
2016-04-24 07:22:04 -04:00
|
|
|
_buttons: buttons,
|
2016-04-09 04:56:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
|
2016-04-09 04:56:55 -04:00
|
|
|
self.elements = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called every frame the screen is active
|
2016-04-21 06:00:05 -04:00
|
|
|
fn tick(&mut self, _delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
|
|
|
|
let elements = self.elements.as_mut().unwrap();
|
|
|
|
{
|
|
|
|
let mode = ui_container.mode;
|
2016-04-24 07:22:04 -04:00
|
|
|
let mut background = elements.background.borrow_mut();
|
|
|
|
background.width = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 854.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.width as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
|
|
|
background.height = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 480.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.height as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
2016-04-21 06:00:05 -04:00
|
|
|
}
|
2016-04-09 04:56:55 -04:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events
|
2016-04-16 16:43:41 -04:00
|
|
|
fn on_scroll(&mut self, _x: f64, _y: f64) {
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_closable(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct AudioSettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
_vars: Rc<console::Vars>,
|
2016-04-21 06:00:05 -04:00
|
|
|
elements: Option<UIElements>
|
2016-04-09 04:56:55 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl AudioSettingsMenu {
|
2016-04-21 06:40:50 -04:00
|
|
|
pub fn new(vars: Rc<console::Vars>) -> AudioSettingsMenu {
|
2016-04-09 04:56:55 -04:00
|
|
|
AudioSettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
_vars: vars,
|
2016-04-09 04:56:55 -04:00
|
|
|
elements: None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl super::Screen for AudioSettingsMenu {
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_active(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
|
|
|
let background = ui::ImageBuilder::new()
|
|
|
|
.texture("steven:solid")
|
|
|
|
.position(0.0, 0.0)
|
|
|
|
.size(854.0, 480.0)
|
|
|
|
.colour((0, 0, 0, 100))
|
|
|
|
.create(ui_container);
|
|
|
|
|
|
|
|
let mut buttons = vec![];
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
|
|
|
|
let done_button = ui::ButtonBuilder::new()
|
|
|
|
.position(0.0, 50.0)
|
|
|
|
.size(300.0, 40.0)
|
|
|
|
.alignment(ui::VAttach::Bottom, ui::HAttach::Center)
|
|
|
|
.create(ui_container);
|
|
|
|
{
|
|
|
|
let mut done_button = done_button.borrow_mut();
|
|
|
|
let txt = ui::TextBuilder::new()
|
|
|
|
.text("Done")
|
|
|
|
.alignment(ui::VAttach::Middle, ui::HAttach::Center)
|
|
|
|
.attach(&mut *done_button);
|
|
|
|
done_button.add_text(txt);
|
|
|
|
done_button.add_click_func(|_, game| {
|
|
|
|
game.screen_sys.pop_screen();
|
|
|
|
game.focused = true;
|
|
|
|
true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
buttons.push(done_button);
|
2016-04-09 06:43:44 -04:00
|
|
|
|
2016-04-21 06:00:05 -04:00
|
|
|
self.elements = Some(UIElements {
|
|
|
|
background: background,
|
2016-04-24 07:22:04 -04:00
|
|
|
_buttons: buttons,
|
2016-04-09 04:56:55 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
}
|
2016-04-24 07:22:04 -04:00
|
|
|
fn on_deactive(&mut self, _renderer: &mut render::Renderer, _ui_container: &mut ui::Container) {
|
2016-04-09 04:56:55 -04:00
|
|
|
self.elements = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called every frame the screen is active
|
2016-04-21 06:00:05 -04:00
|
|
|
fn tick(&mut self, _delta: f64, renderer: &mut render::Renderer, ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
|
|
|
|
let elements = self.elements.as_mut().unwrap();
|
|
|
|
{
|
|
|
|
let mode = ui_container.mode;
|
2016-04-24 07:22:04 -04:00
|
|
|
let mut background = elements.background.borrow_mut();
|
|
|
|
background.width = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 854.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.width as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
|
|
|
background.height = match mode {
|
2016-04-21 06:00:05 -04:00
|
|
|
ui::Mode::Unscaled(scale) => 480.0 / scale,
|
|
|
|
ui::Mode::Scaled => renderer.height as f64,
|
2016-04-24 07:22:04 -04:00
|
|
|
};
|
2016-04-21 06:00:05 -04:00
|
|
|
}
|
2016-04-09 04:56:55 -04:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events
|
2016-04-16 16:43:41 -04:00
|
|
|
fn on_scroll(&mut self, _x: f64, _y: f64) {
|
2016-04-09 04:56:55 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_closable(&self) -> bool {
|
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|