mac preferences is all there!
This commit is contained in:
parent
41ab8c5289
commit
7bf0a8277a
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -452,7 +452,7 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
|||
[[package]]
|
||||
name = "cacao"
|
||||
version = "0.4.0-beta2"
|
||||
source = "git+https://github.com/italicsjenga/cacao#6a3ed259b28f9bfd093293f61cd659247aa7375f"
|
||||
source = "git+https://github.com/italicsjenga/cacao#6144e9f244abfd15687de00b6cfda6b4606f351a"
|
||||
dependencies = [
|
||||
"bitmask-enum",
|
||||
"block2",
|
||||
|
|
|
@ -51,7 +51,6 @@ impl AppDelegate for TwincUiApp {
|
|||
fn did_finish_launching(&self) {
|
||||
App::set_menu(menu());
|
||||
App::activate();
|
||||
self.preferences.read().unwrap().show();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use cacao::{
|
||||
layout::Layout,
|
||||
view::{View, ViewDelegate},
|
||||
|
@ -7,10 +9,23 @@ use gb_emu_lib::config::{Config, ConfigManager, ResolutionOverride};
|
|||
|
||||
use crate::macos::dispatch;
|
||||
|
||||
use self::widgets::{PathView, StepperView, ToggleView};
|
||||
use self::widgets::{PathView, StepperView, StepperViewToggle, ToggleView};
|
||||
|
||||
mod widgets;
|
||||
|
||||
fn make_relative_path(path: PathBuf, base_dir: PathBuf) -> String {
|
||||
let path = path.canonicalize().unwrap_or(path);
|
||||
let base_dir = base_dir.canonicalize().unwrap_or(base_dir);
|
||||
if path.starts_with(&base_dir) {
|
||||
path.strip_prefix(base_dir).unwrap_or(&path)
|
||||
} else {
|
||||
&path
|
||||
}
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
pub(crate) struct CorePreferencesContentView {
|
||||
config: Config,
|
||||
config_manager: ConfigManager,
|
||||
|
@ -20,10 +35,10 @@ pub(crate) struct CorePreferencesContentView {
|
|||
prefer_cgb: ToggleView,
|
||||
dmg_shader: PathView,
|
||||
dmg_resizable: ToggleView,
|
||||
dmg_resolution: StepperView,
|
||||
dmg_resolution: StepperViewToggle,
|
||||
cgb_shader: PathView,
|
||||
cgb_resizable: ToggleView,
|
||||
cgb_resolution: StepperView,
|
||||
cgb_resolution: StepperViewToggle,
|
||||
}
|
||||
|
||||
impl CorePreferencesContentView {
|
||||
|
@ -51,14 +66,16 @@ impl CorePreferencesContentView {
|
|||
}
|
||||
CorePreferencesUpdates::PreferCGB => self.config.prefer_cgb = !self.config.prefer_cgb,
|
||||
CorePreferencesUpdates::DmgResolution => {
|
||||
let val = self.dmg_resolution.update();
|
||||
self.config.vulkan_config.dmg_resolution_override =
|
||||
ResolutionOverride::Scale(val.round() as usize)
|
||||
if let Some(val) = self.dmg_resolution.update() {
|
||||
self.config.vulkan_config.dmg_resolution_override =
|
||||
ResolutionOverride::Scale(val.round() as usize)
|
||||
}
|
||||
}
|
||||
CorePreferencesUpdates::CgbResolution => {
|
||||
let val = self.cgb_resolution.update();
|
||||
self.config.vulkan_config.cgb_resolution_override =
|
||||
ResolutionOverride::Scale(val.round() as usize)
|
||||
if let Some(val) = self.cgb_resolution.update() {
|
||||
self.config.vulkan_config.cgb_resolution_override =
|
||||
ResolutionOverride::Scale(val.round() as usize)
|
||||
}
|
||||
}
|
||||
CorePreferencesUpdates::DmgResizable => {
|
||||
self.config.vulkan_config.dmg_shader_resizable =
|
||||
|
@ -68,6 +85,44 @@ impl CorePreferencesContentView {
|
|||
self.config.vulkan_config.cgb_shader_resizable =
|
||||
!self.config.vulkan_config.cgb_shader_resizable
|
||||
}
|
||||
CorePreferencesUpdates::DmgResolutionEnabled => {
|
||||
self.dmg_resolution.flip();
|
||||
self.config.vulkan_config.dmg_resolution_override = self
|
||||
.dmg_resolution
|
||||
.update()
|
||||
.map(|v| v.round() as usize)
|
||||
.into();
|
||||
}
|
||||
CorePreferencesUpdates::CgbResolutionEnabled => {
|
||||
self.cgb_resolution.flip();
|
||||
self.config.vulkan_config.cgb_resolution_override = self
|
||||
.cgb_resolution
|
||||
.update()
|
||||
.map(|v| v.round() as usize)
|
||||
.into();
|
||||
}
|
||||
CorePreferencesUpdates::DmgBootrom(path) => {
|
||||
self.config.dmg_bootrom =
|
||||
path.map(|v| make_relative_path(v, self.config_manager.dir()));
|
||||
self.dmg_bootrom.update(self.config.dmg_bootrom.clone());
|
||||
}
|
||||
CorePreferencesUpdates::CgbBootrom(path) => {
|
||||
self.config.cgb_bootrom =
|
||||
path.map(|v| make_relative_path(v, self.config_manager.dir()));
|
||||
self.cgb_bootrom.update(self.config.cgb_bootrom.clone());
|
||||
}
|
||||
CorePreferencesUpdates::DmgShader(path) => {
|
||||
self.config.vulkan_config.dmg_shader_path =
|
||||
path.map(|v| make_relative_path(v, self.config_manager.dir()));
|
||||
self.dmg_shader
|
||||
.update(self.config.vulkan_config.dmg_shader_path.clone());
|
||||
}
|
||||
CorePreferencesUpdates::CgbShader(path) => {
|
||||
self.config.vulkan_config.cgb_shader_path =
|
||||
path.map(|v| make_relative_path(v, self.config_manager.dir()));
|
||||
self.cgb_shader
|
||||
.update(self.config.vulkan_config.cgb_shader_path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.config_manager
|
||||
|
@ -80,9 +135,15 @@ pub(crate) enum CorePreferencesUpdates {
|
|||
ShowBootrom,
|
||||
PreferCGB,
|
||||
DmgResolution,
|
||||
DmgResolutionEnabled,
|
||||
CgbResolution,
|
||||
CgbResolutionEnabled,
|
||||
DmgResizable,
|
||||
CgbResizable,
|
||||
DmgBootrom(Option<PathBuf>),
|
||||
CgbBootrom(Option<PathBuf>),
|
||||
DmgShader(Option<PathBuf>),
|
||||
CgbShader(Option<PathBuf>),
|
||||
}
|
||||
|
||||
impl ViewDelegate for CorePreferencesContentView {
|
||||
|
@ -90,11 +151,19 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
|
||||
fn did_load(&mut self, view: View) {
|
||||
self.dmg_bootrom
|
||||
.configure("DMG bootrom:", "", self.config.dmg_bootrom.clone());
|
||||
.configure("DMG bootrom", "", self.config.dmg_bootrom.clone(), |v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::DmgBootrom(v)),
|
||||
))
|
||||
});
|
||||
view.add_subview(&self.dmg_bootrom.view);
|
||||
|
||||
self.cgb_bootrom
|
||||
.configure("CGB bootrom:", "", self.config.cgb_bootrom.clone());
|
||||
.configure("CGB bootrom", "", self.config.cgb_bootrom.clone(), |v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::CgbBootrom(v)),
|
||||
))
|
||||
});
|
||||
view.add_subview(&self.cgb_bootrom.view);
|
||||
|
||||
self.show_bootrom
|
||||
|
@ -121,18 +190,27 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
ResolutionOverride::Default => 4,
|
||||
};
|
||||
|
||||
self.dmg_resolution.set_suffix(String::from("x"));
|
||||
self.dmg_resolution.configure(
|
||||
"DMG resolution scale",
|
||||
"DMG scale override",
|
||||
1.,
|
||||
10.,
|
||||
1.0,
|
||||
0,
|
||||
Some(dmg_resolution_override as f64),
|
||||
self.config.vulkan_config.dmg_resolution_override != ResolutionOverride::Default,
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::DmgResolution),
|
||||
));
|
||||
},
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(
|
||||
CorePreferencesUpdates::DmgResolutionEnabled,
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.dmg_resolution.view);
|
||||
|
||||
|
@ -140,6 +218,11 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
"DMG shader",
|
||||
"",
|
||||
self.config.vulkan_config.dmg_shader_path.clone(),
|
||||
|v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::DmgShader(v)),
|
||||
))
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.dmg_shader.view);
|
||||
|
||||
|
@ -159,18 +242,27 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
ResolutionOverride::Default => 4,
|
||||
};
|
||||
|
||||
self.cgb_resolution.set_suffix(String::from("x"));
|
||||
self.cgb_resolution.configure(
|
||||
"CGB resolution scale",
|
||||
"CGB scale override",
|
||||
1.,
|
||||
10.,
|
||||
1.0,
|
||||
0,
|
||||
Some(cgb_resolution_override as f64),
|
||||
self.config.vulkan_config.cgb_resolution_override != ResolutionOverride::Default,
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::CgbResolution),
|
||||
));
|
||||
},
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(
|
||||
CorePreferencesUpdates::CgbResolutionEnabled,
|
||||
),
|
||||
));
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.cgb_resolution.view);
|
||||
|
||||
|
@ -178,6 +270,11 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
"CGB shader",
|
||||
"",
|
||||
self.config.vulkan_config.cgb_shader_path.clone(),
|
||||
|v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateCore(CorePreferencesUpdates::CgbShader(v)),
|
||||
))
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.cgb_shader.view);
|
||||
|
||||
|
@ -213,7 +310,10 @@ impl ViewDelegate for CorePreferencesContentView {
|
|||
pub(crate) struct StandalonePreferencesContentView {
|
||||
config: StandaloneConfig,
|
||||
config_manager: ConfigManager,
|
||||
scale_factor: StepperView,
|
||||
group_screenshots_by_rom: ToggleView,
|
||||
buffers_per_frame: StepperView,
|
||||
output_buffer_size: StepperView,
|
||||
}
|
||||
|
||||
impl StandalonePreferencesContentView {
|
||||
|
@ -222,6 +322,9 @@ impl StandalonePreferencesContentView {
|
|||
config: config_manager.load_or_create_config(),
|
||||
config_manager,
|
||||
group_screenshots_by_rom: Default::default(),
|
||||
scale_factor: Default::default(),
|
||||
buffers_per_frame: Default::default(),
|
||||
output_buffer_size: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,6 +333,18 @@ impl StandalonePreferencesContentView {
|
|||
StandalonePreferencesUpdates::GroupScreenshotsByRom => {
|
||||
self.config.group_screenshots_by_rom = !self.config.group_screenshots_by_rom
|
||||
}
|
||||
StandalonePreferencesUpdates::ScaleFactor => {
|
||||
self.config.scale_factor = self.scale_factor.update().round() as usize
|
||||
}
|
||||
StandalonePreferencesUpdates::BuffersPerFrame => {
|
||||
self.config.buffers_per_frame = self.buffers_per_frame.update().round() as usize
|
||||
}
|
||||
StandalonePreferencesUpdates::OutputBufferSize => {
|
||||
self.config.output_buffer_size = self.output_buffer_size.update_map(|v| {
|
||||
let i = v.round() as u32;
|
||||
2_u32.pow(i)
|
||||
});
|
||||
}
|
||||
}
|
||||
self.config_manager
|
||||
.save_custom_config(self.config.clone())
|
||||
|
@ -239,12 +354,32 @@ impl StandalonePreferencesContentView {
|
|||
|
||||
pub(crate) enum StandalonePreferencesUpdates {
|
||||
GroupScreenshotsByRom,
|
||||
ScaleFactor,
|
||||
BuffersPerFrame,
|
||||
OutputBufferSize,
|
||||
}
|
||||
|
||||
impl ViewDelegate for StandalonePreferencesContentView {
|
||||
const NAME: &'static str = "StandalonePreferencesContentView";
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
self.scale_factor.configure(
|
||||
"Scale factor",
|
||||
1.,
|
||||
16.,
|
||||
1.,
|
||||
0,
|
||||
Some(self.config.scale_factor as f64),
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateStandalone(
|
||||
StandalonePreferencesUpdates::ScaleFactor,
|
||||
),
|
||||
))
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.scale_factor.view);
|
||||
|
||||
self.group_screenshots_by_rom.configure(
|
||||
"Group screenshots by ROM",
|
||||
self.config.group_screenshots_by_rom,
|
||||
|
@ -256,16 +391,63 @@ impl ViewDelegate for StandalonePreferencesContentView {
|
|||
))
|
||||
},
|
||||
);
|
||||
|
||||
view.add_subview(&self.group_screenshots_by_rom.view);
|
||||
|
||||
widgets::auto_layout(&view, vec![&self.group_screenshots_by_rom.view]);
|
||||
self.buffers_per_frame.configure(
|
||||
"Buffers per frame",
|
||||
1.,
|
||||
10.,
|
||||
1.,
|
||||
0,
|
||||
Some(self.config.buffers_per_frame as f64),
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateStandalone(
|
||||
StandalonePreferencesUpdates::BuffersPerFrame,
|
||||
),
|
||||
))
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.buffers_per_frame.view);
|
||||
|
||||
self.output_buffer_size.configure(
|
||||
"Output buffer size",
|
||||
1.,
|
||||
100.,
|
||||
1.,
|
||||
0,
|
||||
Some((self.config.output_buffer_size as f64).log2()),
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateStandalone(
|
||||
StandalonePreferencesUpdates::OutputBufferSize,
|
||||
),
|
||||
))
|
||||
},
|
||||
);
|
||||
self.output_buffer_size.update_map(|v| {
|
||||
let i = v.round() as u32;
|
||||
2_u32.pow(i)
|
||||
});
|
||||
view.add_subview(&self.output_buffer_size.view);
|
||||
|
||||
widgets::auto_layout(
|
||||
&view,
|
||||
vec![
|
||||
&self.scale_factor.view,
|
||||
&self.group_screenshots_by_rom.view,
|
||||
&self.buffers_per_frame.view,
|
||||
&self.output_buffer_size.view,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct VstPreferencesContentView {
|
||||
config: twinc_emu_vst::VstConfig,
|
||||
config_manager: ConfigManager,
|
||||
scale_factor: StepperView,
|
||||
rom: PathView,
|
||||
force_skip_bootrom: ToggleView,
|
||||
}
|
||||
|
||||
|
@ -275,6 +457,8 @@ impl VstPreferencesContentView {
|
|||
config: config_manager.load_or_create_config(),
|
||||
config_manager,
|
||||
force_skip_bootrom: Default::default(),
|
||||
scale_factor: Default::default(),
|
||||
rom: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -283,6 +467,15 @@ impl VstPreferencesContentView {
|
|||
VstPreferencesUpdates::ForceSkipBootrom => {
|
||||
self.config.force_skip_bootrom = !self.config.force_skip_bootrom
|
||||
}
|
||||
VstPreferencesUpdates::ScaleFactor => {
|
||||
self.config.scale_factor = self.scale_factor.update().round() as usize
|
||||
}
|
||||
VstPreferencesUpdates::Rom(path) => {
|
||||
if let Some(path) = path {
|
||||
self.config.rom = make_relative_path(path, self.config_manager.dir());
|
||||
}
|
||||
self.rom.update(Some(self.config.rom.clone()));
|
||||
}
|
||||
}
|
||||
self.config_manager
|
||||
.save_custom_config(self.config.clone())
|
||||
|
@ -292,12 +485,37 @@ impl VstPreferencesContentView {
|
|||
|
||||
pub(crate) enum VstPreferencesUpdates {
|
||||
ForceSkipBootrom,
|
||||
ScaleFactor,
|
||||
Rom(Option<PathBuf>),
|
||||
}
|
||||
|
||||
impl ViewDelegate for VstPreferencesContentView {
|
||||
const NAME: &'static str = "VstPreferencesContentView";
|
||||
|
||||
fn did_load(&mut self, view: View) {
|
||||
self.scale_factor.configure(
|
||||
"Scale factor",
|
||||
1.,
|
||||
16.,
|
||||
1.,
|
||||
0,
|
||||
Some(self.config.scale_factor as f64),
|
||||
|_v| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateVst(VstPreferencesUpdates::ScaleFactor),
|
||||
))
|
||||
},
|
||||
);
|
||||
view.add_subview(&self.scale_factor.view);
|
||||
|
||||
self.rom
|
||||
.configure("ROM", "", Some(self.config.rom.clone()), |path| {
|
||||
dispatch(crate::macos::AppMessage::Preferences(
|
||||
super::PreferencesMessage::UpdateVst(VstPreferencesUpdates::Rom(path)),
|
||||
))
|
||||
});
|
||||
view.add_subview(&self.rom.view);
|
||||
|
||||
self.force_skip_bootrom.configure(
|
||||
"Force skip bootrom",
|
||||
self.config.force_skip_bootrom,
|
||||
|
@ -307,9 +525,15 @@ impl ViewDelegate for VstPreferencesContentView {
|
|||
))
|
||||
},
|
||||
);
|
||||
|
||||
view.add_subview(&self.force_skip_bootrom.view);
|
||||
|
||||
widgets::auto_layout(&view, vec![&self.force_skip_bootrom.view]);
|
||||
widgets::auto_layout(
|
||||
&view,
|
||||
vec![
|
||||
&self.scale_factor.view,
|
||||
&self.rom.view,
|
||||
&self.force_skip_bootrom.view,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use std::fmt::Display;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use cacao::button::Button;
|
||||
use cacao::filesystem::FileSelectPanel;
|
||||
use cacao::input::TextField;
|
||||
use cacao::layout::{Layout, LayoutConstraint};
|
||||
use cacao::select::Select;
|
||||
|
@ -56,12 +59,15 @@ impl Default for ToggleView {
|
|||
view.add_subview(&title);
|
||||
|
||||
LayoutConstraint::activate(&[
|
||||
switch.top.constraint_equal_to(&view.top),
|
||||
switch.center_y.constraint_equal_to(&view.center_y),
|
||||
switch
|
||||
.leading
|
||||
.constraint_equal_to(&view.leading)
|
||||
.offset(LEFT_MARGIN),
|
||||
switch.width.constraint_equal_to_constant(24.),
|
||||
title
|
||||
.width
|
||||
.constraint_greater_than_or_equal_to_constant(200.),
|
||||
title.top.constraint_equal_to(&view.top),
|
||||
title.leading.constraint_equal_to(&switch.trailing),
|
||||
title.trailing.constraint_equal_to(&view.trailing),
|
||||
|
@ -90,10 +96,13 @@ impl ToggleView {
|
|||
pub struct PathView {
|
||||
pub view: View,
|
||||
pub field: TextField,
|
||||
pub button: Button,
|
||||
pub browse_button: Button,
|
||||
pub clear_button: Button,
|
||||
pub title: Label,
|
||||
}
|
||||
|
||||
const PATH_VIEW_BUTTON_WIDTH: f64 = 80.;
|
||||
|
||||
impl Default for PathView {
|
||||
fn default() -> Self {
|
||||
let view = View::new();
|
||||
|
@ -104,8 +113,12 @@ impl Default for PathView {
|
|||
field.set_line_break_mode(cacao::text::LineBreakMode::TruncateMiddle);
|
||||
view.add_subview(&field);
|
||||
|
||||
let button = Button::new("Browse");
|
||||
view.add_subview(&button);
|
||||
let browse_button = Button::new("Browse");
|
||||
view.add_subview(&browse_button);
|
||||
|
||||
let clear_button = Button::new("Clear");
|
||||
view.add_subview(&clear_button);
|
||||
|
||||
let title = Label::new();
|
||||
view.add_subview(&title);
|
||||
title.set_text_alignment(cacao::text::TextAlign::Right);
|
||||
|
@ -122,33 +135,68 @@ impl Default for PathView {
|
|||
field.bottom.constraint_equal_to(&view.bottom),
|
||||
field
|
||||
.trailing
|
||||
.constraint_equal_to(&button.leading)
|
||||
.constraint_equal_to(&browse_button.leading)
|
||||
.offset(-10.),
|
||||
field
|
||||
.width
|
||||
.constraint_greater_than_or_equal_to_constant(200.),
|
||||
button.center_y.constraint_equal_to(&view.center_y),
|
||||
button.trailing.constraint_equal_to(&view.trailing),
|
||||
button.width.constraint_equal_to_constant(100.),
|
||||
browse_button.center_y.constraint_equal_to(&view.center_y),
|
||||
browse_button
|
||||
.trailing
|
||||
.constraint_equal_to(&clear_button.leading)
|
||||
.offset(-10.),
|
||||
browse_button
|
||||
.width
|
||||
.constraint_equal_to_constant(PATH_VIEW_BUTTON_WIDTH),
|
||||
clear_button.center_y.constraint_equal_to(&view.center_y),
|
||||
clear_button.trailing.constraint_equal_to(&view.trailing),
|
||||
clear_button
|
||||
.width
|
||||
.constraint_equal_to_constant(PATH_VIEW_BUTTON_WIDTH),
|
||||
]);
|
||||
|
||||
Self {
|
||||
view,
|
||||
field,
|
||||
button,
|
||||
browse_button,
|
||||
clear_button,
|
||||
title,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PathView {
|
||||
pub fn configure(&mut self, title: &str, placeholder: &str, state: Option<String>) {
|
||||
pub fn configure<F>(
|
||||
&mut self,
|
||||
title: &str,
|
||||
placeholder: &str,
|
||||
state: Option<String>,
|
||||
handler: F,
|
||||
) where
|
||||
F: Fn(Option<PathBuf>) + Copy + Send + Sync + 'static,
|
||||
{
|
||||
self.browse_button.set_action(move |_v| {
|
||||
let mut file_select_panel = FileSelectPanel::new();
|
||||
file_select_panel.set_can_choose_directories(false);
|
||||
file_select_panel.set_can_choose_files(true);
|
||||
file_select_panel.set_allows_multiple_selection(false);
|
||||
file_select_panel.show(move |v| {
|
||||
handler(v.first().map(|v| v.pathbuf()));
|
||||
});
|
||||
});
|
||||
|
||||
self.clear_button.set_action(move |_v| handler(None));
|
||||
|
||||
self.title.set_text(title);
|
||||
self.field.set_placeholder_text(placeholder);
|
||||
if let Some(state) = state {
|
||||
self.field.set_text(&state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, state: Option<String>) {
|
||||
self.field.set_text(&state.unwrap_or(String::from("")));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PickerView<T>
|
||||
|
@ -301,4 +349,137 @@ impl StepperView {
|
|||
.set_text(format!("{:.1$}", val, self.decimal_places).as_str());
|
||||
val
|
||||
}
|
||||
|
||||
pub fn update_map<F, T>(&mut self, map: F) -> T
|
||||
where
|
||||
F: Fn(f64) -> T,
|
||||
T: Display,
|
||||
{
|
||||
let mapped = map(self.stepper.get_value());
|
||||
self.field.set_text(format!("{}", mapped).as_str());
|
||||
mapped
|
||||
}
|
||||
}
|
||||
|
||||
pub struct StepperViewToggle {
|
||||
pub view: View,
|
||||
pub switch: Switch,
|
||||
pub field: TextField,
|
||||
pub stepper: Stepper,
|
||||
pub title: Label,
|
||||
pub decimal_places: usize,
|
||||
pub enabled: bool,
|
||||
pub suffix: String,
|
||||
}
|
||||
|
||||
impl Default for StepperViewToggle {
|
||||
fn default() -> Self {
|
||||
let view = View::new();
|
||||
|
||||
let stepper = Stepper::new();
|
||||
stepper.set_wraps(false);
|
||||
view.add_subview(&stepper);
|
||||
|
||||
let field = TextField::new();
|
||||
field.set_uses_single_line(true);
|
||||
field.set_editable(false);
|
||||
view.add_subview(&field);
|
||||
|
||||
let title = Label::new();
|
||||
view.add_subview(&title);
|
||||
title.set_text_alignment(cacao::text::TextAlign::Right);
|
||||
|
||||
let switch = Switch::new("");
|
||||
view.add_subview(&switch);
|
||||
|
||||
LayoutConstraint::activate(&[
|
||||
title.center_y.constraint_equal_to(&view.center_y),
|
||||
title.leading.constraint_equal_to(&view.leading),
|
||||
title.width.constraint_equal_to_constant(LEFT_MARGIN - 10.),
|
||||
switch.center_y.constraint_equal_to(&view.center_y),
|
||||
switch
|
||||
.leading
|
||||
.constraint_equal_to(&title.trailing)
|
||||
.offset(10.),
|
||||
field.top.constraint_equal_to(&view.top),
|
||||
field
|
||||
.leading
|
||||
.constraint_equal_to(&switch.trailing)
|
||||
.offset(10.),
|
||||
stepper.center_y.constraint_equal_to(&view.center_y),
|
||||
stepper
|
||||
.leading
|
||||
.constraint_equal_to(&field.trailing)
|
||||
.offset(5.),
|
||||
field.bottom.constraint_equal_to(&view.bottom),
|
||||
field
|
||||
.width
|
||||
.constraint_greater_than_or_equal_to_constant(80.),
|
||||
]);
|
||||
|
||||
Self {
|
||||
view,
|
||||
switch,
|
||||
field,
|
||||
stepper,
|
||||
title,
|
||||
decimal_places: 0,
|
||||
enabled: false,
|
||||
suffix: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl StepperViewToggle {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn configure<F, G>(
|
||||
&mut self,
|
||||
title: &str,
|
||||
min: f64,
|
||||
max: f64,
|
||||
increment: f64,
|
||||
decimal_places: usize,
|
||||
initial_value: Option<f64>,
|
||||
initial_enabled: bool,
|
||||
stepper_handler: F,
|
||||
switch_handler: G,
|
||||
) where
|
||||
F: Fn(*const Object) + Send + Sync + 'static,
|
||||
G: Fn(*const Object) + Send + Sync + 'static,
|
||||
{
|
||||
self.title.set_text(title);
|
||||
self.decimal_places = decimal_places;
|
||||
if let Some(val) = initial_value {
|
||||
self.stepper.set_value(val);
|
||||
}
|
||||
self.stepper.set_min_value(min);
|
||||
self.stepper.set_max_value(max);
|
||||
self.stepper.set_increment(increment);
|
||||
self.stepper.set_action(stepper_handler);
|
||||
self.switch.set_action(switch_handler);
|
||||
self.enabled = initial_enabled;
|
||||
self.update();
|
||||
}
|
||||
|
||||
pub fn set_suffix(&mut self, suffix: String) {
|
||||
self.suffix = suffix;
|
||||
}
|
||||
|
||||
pub fn update(&mut self) -> Option<f64> {
|
||||
self.switch.set_checked(self.enabled);
|
||||
self.field.set_enabled(self.enabled);
|
||||
if self.enabled {
|
||||
let val = self.stepper.get_value();
|
||||
self.field
|
||||
.set_text(format!("{:.1$}{2}", val, self.decimal_places, self.suffix,).as_str());
|
||||
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flip(&mut self) {
|
||||
self.enabled = !self.enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,12 +117,21 @@ pub struct VulkanRendererConfig {
|
|||
pub cgb_resolution_override: ResolutionOverride,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
|
||||
pub enum ResolutionOverride {
|
||||
Scale(usize),
|
||||
Default,
|
||||
}
|
||||
|
||||
impl From<Option<usize>> for ResolutionOverride {
|
||||
fn from(value: Option<usize>) -> Self {
|
||||
match value {
|
||||
Some(scale) => Self::Scale(scale),
|
||||
None => Self::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
Loading…
Reference in a new issue