237 lines
7.4 KiB
Rust
237 lines
7.4 KiB
Rust
use druid::im::Vector;
|
|
use druid::widget::{
|
|
Align, Button, CrossAxisAlignment, Flex, Label, List, MainAxisAlignment, Maybe, Scroll, Split,
|
|
ViewSwitcher,
|
|
};
|
|
use druid::{lens, Insets, LensExt, Widget, WidgetExt, WindowDesc};
|
|
|
|
use crate::state::{ModInfo, State, View};
|
|
use crate::theme;
|
|
use crate::widget::ExtraWidgetExt;
|
|
|
|
const TITLE: &str = "Darktide Mod Manager";
|
|
const WINDOW_WIDTH: f64 = 800.0;
|
|
const WINDOW_HEIGHT: f64 = 600.0;
|
|
const MOD_DETAILS_MIN_WIDTH: f64 = 325.0;
|
|
|
|
pub(crate) fn new() -> WindowDesc<State> {
|
|
WindowDesc::new(build_window())
|
|
.title(TITLE)
|
|
.window_size((WINDOW_WIDTH, WINDOW_HEIGHT))
|
|
}
|
|
|
|
fn build_top_bar() -> impl Widget<State> {
|
|
Flex::row()
|
|
.must_fill_main_axis(true)
|
|
.main_axis_alignment(MainAxisAlignment::SpaceBetween)
|
|
.with_child(
|
|
Flex::row()
|
|
.with_child(
|
|
Button::new("Mods").on_click(|_ctx, state: &mut State, _env| {
|
|
state.set_current_view(View::Mods)
|
|
}),
|
|
)
|
|
.with_default_spacer()
|
|
.with_child(
|
|
Button::new("Settings").on_click(|_ctx, state: &mut State, _env| {
|
|
state.set_current_view(View::Settings)
|
|
}),
|
|
)
|
|
.with_default_spacer()
|
|
.with_child(
|
|
Button::new("About").on_click(|_ctx, state: &mut State, _env| {
|
|
state.set_current_view(View::About)
|
|
}),
|
|
),
|
|
)
|
|
.with_child(
|
|
Flex::row()
|
|
.with_child(Button::new("Deploy Mods").on_click(
|
|
|_ctx, _state: &mut State, _env| {
|
|
todo!();
|
|
},
|
|
))
|
|
.with_default_spacer()
|
|
.with_child(
|
|
Button::new("Run Game").on_click(|_ctx, _state: &mut State, _env| {
|
|
todo!();
|
|
}),
|
|
),
|
|
)
|
|
.padding(theme::TOP_BAR_INSETS)
|
|
.background(theme::TOP_BAR_BACKGROUND_COLOR)
|
|
// TODO: Add bottom border. Need a custom widget for that, as the built-in only provides
|
|
// uniform borders on all sides
|
|
}
|
|
|
|
fn build_mod_list() -> impl Widget<State> {
|
|
let list = List::new(|| {
|
|
Flex::row()
|
|
.must_fill_main_axis(true)
|
|
// .with_child(
|
|
// Label::dynamic(|enabled, _env| {
|
|
// if *enabled {
|
|
// "Enabled".into()
|
|
// } else {
|
|
// "Disabled".into()
|
|
// }
|
|
// })
|
|
// .lens(
|
|
// lens::Identity
|
|
// .map(
|
|
// |(i, info)| info,
|
|
// |(i, info), new_info| {
|
|
// todo!();
|
|
// },
|
|
// )
|
|
// .then(ModInfo::enabled),
|
|
// ),
|
|
// )
|
|
// .with_child(Label::raw().lens(ModInfo::name))
|
|
.on_click(|_ctx, state, _env| {
|
|
todo!();
|
|
})
|
|
});
|
|
|
|
Scroll::new(list)
|
|
.vertical()
|
|
.lens(State::mods.map(
|
|
|mods| {
|
|
mods.iter()
|
|
.enumerate()
|
|
.map(|(i, val)| (i, val.clone()))
|
|
.collect::<Vector<_>>()
|
|
},
|
|
|mods, infos| {
|
|
infos.into_iter().for_each(|(i, info)| {
|
|
mods.set(i, info);
|
|
});
|
|
},
|
|
))
|
|
.content_must_fill()
|
|
}
|
|
|
|
fn build_mod_details() -> impl Widget<State> {
|
|
let details_container = Maybe::new(
|
|
|| {
|
|
Flex::column()
|
|
.cross_axis_alignment(CrossAxisAlignment::Start)
|
|
.with_child(Label::raw().lens(ModInfo::name))
|
|
.with_flex_child(Label::raw().lens(ModInfo::description), 1.0)
|
|
},
|
|
Flex::column,
|
|
)
|
|
.lens(State::selected_mod);
|
|
|
|
let button_move_up = Button::new("Move Up")
|
|
.on_click(|_ctx, index: &mut Option<usize>, _env| {
|
|
if let Some(i) = index.as_mut() {
|
|
*i = i.saturating_sub(1)
|
|
}
|
|
})
|
|
.lens(State::selected_mod_index);
|
|
|
|
let button_move_down = Button::new("Move Down")
|
|
.on_click(|_ctx, index: &mut Option<usize>, _env| {
|
|
if let Some(i) = index.as_mut() {
|
|
*i = i.saturating_add(1)
|
|
}
|
|
})
|
|
.lens(State::selected_mod_index);
|
|
|
|
let button_toggle_mod = Maybe::new(
|
|
|| {
|
|
Button::dynamic(|enabled, _env| {
|
|
if *enabled {
|
|
"Disable Mod".into()
|
|
} else {
|
|
"Enabled Mod".into()
|
|
}
|
|
})
|
|
.on_click(|_ctx, info: &mut bool, _env| {
|
|
*info = !*info;
|
|
})
|
|
.lens(ModInfo::enabled)
|
|
},
|
|
// TODO: Gray out
|
|
|| Button::new("Enable Mod"),
|
|
)
|
|
.lens(State::selected_mod);
|
|
|
|
let button_add_mod = Button::new("Add Mod").on_click(|_ctx, state: &mut State, _env| {
|
|
// TODO: Implement properly
|
|
let info = ModInfo::new();
|
|
state.add_mod(info);
|
|
});
|
|
|
|
let button_delete_mod = Button::new("Delete Mod")
|
|
.on_click(|_ctx, data: &mut State, _env| data.delete_selected_mod());
|
|
|
|
let buttons = Flex::column()
|
|
.with_child(
|
|
Flex::row()
|
|
.main_axis_alignment(MainAxisAlignment::End)
|
|
.with_child(button_move_up)
|
|
.with_default_spacer()
|
|
.with_child(button_move_down)
|
|
.padding(Insets::uniform_xy(5.0, 2.0)),
|
|
)
|
|
.with_child(
|
|
Flex::row()
|
|
.main_axis_alignment(MainAxisAlignment::End)
|
|
.with_child(button_toggle_mod)
|
|
.with_default_spacer()
|
|
.with_child(button_add_mod)
|
|
.with_default_spacer()
|
|
.with_child(button_delete_mod)
|
|
.padding(Insets::uniform_xy(5.0, 2.0)),
|
|
)
|
|
.with_default_spacer();
|
|
|
|
Flex::column()
|
|
.must_fill_main_axis(true)
|
|
.main_axis_alignment(MainAxisAlignment::SpaceBetween)
|
|
.with_flex_child(details_container, 1.0)
|
|
.with_child(buttons)
|
|
}
|
|
|
|
fn build_view_mods() -> impl Widget<State> {
|
|
Split::columns(build_mod_list(), build_mod_details())
|
|
.split_point(0.75)
|
|
.min_size(0.0, MOD_DETAILS_MIN_WIDTH)
|
|
.solid_bar(true)
|
|
.bar_size(2.0)
|
|
.draggable(true)
|
|
}
|
|
|
|
fn build_view_settings() -> impl Widget<State> {
|
|
Label::new("Settings")
|
|
}
|
|
|
|
fn build_view_about() -> impl Widget<State> {
|
|
Align::centered(
|
|
Flex::column()
|
|
.with_child(Label::new("Darktide Mod Manager"))
|
|
.with_child(Label::new(
|
|
"Website: https://git.sclu1034.dev/bitsquid_dt/dtmt",
|
|
)),
|
|
)
|
|
}
|
|
|
|
fn build_main() -> impl Widget<State> {
|
|
ViewSwitcher::new(
|
|
|state: &State, _env| state.get_current_view(),
|
|
|selector, _state, _env| match selector {
|
|
View::Mods => Box::new(build_view_mods()),
|
|
View::Settings => Box::new(build_view_settings()),
|
|
View::About => Box::new(build_view_about()),
|
|
},
|
|
)
|
|
}
|
|
|
|
fn build_window() -> impl Widget<State> {
|
|
Flex::column()
|
|
.must_fill_main_axis(true)
|
|
.with_child(build_top_bar())
|
|
.with_flex_child(build_main(), 1.0)
|
|
}
|