WIP
This commit is contained in:
parent
50569a3c56
commit
a25a6b2917
7 changed files with 356 additions and 65 deletions
47
crates/dtmm/src/controller.rs
Normal file
47
crates/dtmm/src/controller.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use druid::widget::{Button, Controller};
|
||||||
|
use druid::{Data, Env, Event, EventCtx, LifeCycle, LifeCycleCtx, UpdateCtx, Widget};
|
||||||
|
|
||||||
|
pub struct DisabledButtonController;
|
||||||
|
|
||||||
|
impl<T: Data> Controller<T, Button<T>> for DisabledButtonController {
|
||||||
|
fn event(
|
||||||
|
&mut self,
|
||||||
|
child: &mut Button<T>,
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
event: &Event,
|
||||||
|
data: &mut T,
|
||||||
|
env: &Env,
|
||||||
|
) {
|
||||||
|
if !ctx.is_disabled() {
|
||||||
|
ctx.set_disabled(true);
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
child.event(ctx, event, data, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lifecycle(
|
||||||
|
&mut self,
|
||||||
|
child: &mut Button<T>,
|
||||||
|
ctx: &mut LifeCycleCtx,
|
||||||
|
event: &LifeCycle,
|
||||||
|
data: &T,
|
||||||
|
env: &Env,
|
||||||
|
) {
|
||||||
|
child.lifecycle(ctx, event, data, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(
|
||||||
|
&mut self,
|
||||||
|
child: &mut Button<T>,
|
||||||
|
ctx: &mut UpdateCtx,
|
||||||
|
old_data: &T,
|
||||||
|
data: &T,
|
||||||
|
env: &Env,
|
||||||
|
) {
|
||||||
|
if !ctx.is_disabled() {
|
||||||
|
ctx.set_disabled(true);
|
||||||
|
ctx.request_paint();
|
||||||
|
}
|
||||||
|
child.update(ctx, old_data, data, env)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
use clap::command;
|
use clap::command;
|
||||||
use color_eyre::Report;
|
use color_eyre::Report;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
@ -8,6 +10,7 @@ use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
||||||
|
mod controller;
|
||||||
mod main_window;
|
mod main_window;
|
||||||
mod state;
|
mod state;
|
||||||
mod theme;
|
mod theme;
|
||||||
|
|
|
@ -5,19 +5,21 @@ use druid::widget::{
|
||||||
};
|
};
|
||||||
use druid::{lens, Insets, LensExt, Widget, WidgetExt, WindowDesc};
|
use druid::{lens, Insets, LensExt, Widget, WidgetExt, WindowDesc};
|
||||||
|
|
||||||
use crate::state::{ModInfo, State, View};
|
use crate::state::{
|
||||||
|
ModInfo, State, StateController, View, ACTION_DELETE_SELECTED_MOD, ACTION_SELECTED_MOD_DOWN,
|
||||||
|
ACTION_SELECTED_MOD_UP, ACTION_SELECT_MOD,
|
||||||
|
};
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
use crate::widget::ExtraWidgetExt;
|
use crate::widget::ExtraWidgetExt;
|
||||||
|
|
||||||
const TITLE: &str = "Darktide Mod Manager";
|
const TITLE: &str = "Darktide Mod Manager";
|
||||||
const WINDOW_WIDTH: f64 = 800.0;
|
const WINDOW_SIZE: (f64, f64) = (800.0, 600.0);
|
||||||
const WINDOW_HEIGHT: f64 = 600.0;
|
|
||||||
const MOD_DETAILS_MIN_WIDTH: f64 = 325.0;
|
const MOD_DETAILS_MIN_WIDTH: f64 = 325.0;
|
||||||
|
|
||||||
pub(crate) fn new() -> WindowDesc<State> {
|
pub(crate) fn new() -> WindowDesc<State> {
|
||||||
WindowDesc::new(build_window())
|
WindowDesc::new(build_window())
|
||||||
.title(TITLE)
|
.title(TITLE)
|
||||||
.window_size((WINDOW_WIDTH, WINDOW_HEIGHT))
|
.window_size(WINDOW_SIZE)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_top_bar() -> impl Widget<State> {
|
fn build_top_bar() -> impl Widget<State> {
|
||||||
|
@ -33,9 +35,11 @@ fn build_top_bar() -> impl Widget<State> {
|
||||||
)
|
)
|
||||||
.with_default_spacer()
|
.with_default_spacer()
|
||||||
.with_child(
|
.with_child(
|
||||||
Button::new("Settings").on_click(|_ctx, state: &mut State, _env| {
|
Button::new("Settings")
|
||||||
state.set_current_view(View::Settings)
|
.on_click(|_ctx, state: &mut State, _env| {
|
||||||
}),
|
state.set_current_view(View::Settings)
|
||||||
|
})
|
||||||
|
.hidden_if(|_, _| true),
|
||||||
)
|
)
|
||||||
.with_default_spacer()
|
.with_default_spacer()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -68,32 +72,21 @@ fn build_mod_list() -> impl Widget<State> {
|
||||||
let list = List::new(|| {
|
let list = List::new(|| {
|
||||||
Flex::row()
|
Flex::row()
|
||||||
.must_fill_main_axis(true)
|
.must_fill_main_axis(true)
|
||||||
// .with_child(
|
.with_child(
|
||||||
// Label::dynamic(|enabled, _env| {
|
Label::dynamic(|enabled, _env| {
|
||||||
// if *enabled {
|
if *enabled {
|
||||||
// "Enabled".into()
|
"Enabled".into()
|
||||||
// } else {
|
} else {
|
||||||
// "Disabled".into()
|
"Disabled".into()
|
||||||
// }
|
}
|
||||||
// })
|
})
|
||||||
// .lens(
|
.lens(lens!((usize, ModInfo), 1).then(ModInfo::enabled)),
|
||||||
// lens::Identity
|
)
|
||||||
// .map(
|
.with_child(Label::raw().lens(lens!((usize, ModInfo), 1).then(ModInfo::name)))
|
||||||
// |(i, info)| info,
|
.on_click(|ctx, (i, _info), _env| ctx.submit_notification(ACTION_SELECT_MOD.with(*i)))
|
||||||
// |(i, info), new_info| {
|
|
||||||
// todo!();
|
|
||||||
// },
|
|
||||||
// )
|
|
||||||
// .then(ModInfo::enabled),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
// .with_child(Label::raw().lens(ModInfo::name))
|
|
||||||
.on_click(|_ctx, state, _env| {
|
|
||||||
todo!();
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Scroll::new(list)
|
let scroll = Scroll::new(list)
|
||||||
.vertical()
|
.vertical()
|
||||||
.lens(State::mods.map(
|
.lens(State::mods.map(
|
||||||
|mods| {
|
|mods| {
|
||||||
|
@ -108,7 +101,12 @@ fn build_mod_list() -> impl Widget<State> {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
.content_must_fill()
|
.content_must_fill();
|
||||||
|
|
||||||
|
Flex::column()
|
||||||
|
.must_fill_main_axis(true)
|
||||||
|
.with_child(Flex::row())
|
||||||
|
.with_flex_child(scroll, 1.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_mod_details() -> impl Widget<State> {
|
fn build_mod_details() -> impl Widget<State> {
|
||||||
|
@ -121,23 +119,16 @@ fn build_mod_details() -> impl Widget<State> {
|
||||||
},
|
},
|
||||||
Flex::column,
|
Flex::column,
|
||||||
)
|
)
|
||||||
|
.padding(Insets::uniform_xy(5.0, 1.0))
|
||||||
.lens(State::selected_mod);
|
.lens(State::selected_mod);
|
||||||
|
|
||||||
let button_move_up = Button::new("Move Up")
|
let button_move_up = Button::new("Move Up")
|
||||||
.on_click(|_ctx, index: &mut Option<usize>, _env| {
|
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_SELECTED_MOD_UP))
|
||||||
if let Some(i) = index.as_mut() {
|
.disabled_if(|state: &State, _env: &druid::Env| state.can_move_mod_up());
|
||||||
*i = i.saturating_sub(1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.lens(State::selected_mod_index);
|
|
||||||
|
|
||||||
let button_move_down = Button::new("Move Down")
|
let button_move_down = Button::new("Move Down")
|
||||||
.on_click(|_ctx, index: &mut Option<usize>, _env| {
|
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_SELECTED_MOD_DOWN))
|
||||||
if let Some(i) = index.as_mut() {
|
.disabled_if(|state: &State, _env: &druid::Env| state.can_move_mod_down());
|
||||||
*i = i.saturating_add(1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.lens(State::selected_mod_index);
|
|
||||||
|
|
||||||
let button_toggle_mod = Maybe::new(
|
let button_toggle_mod = Maybe::new(
|
||||||
|| {
|
|| {
|
||||||
|
@ -145,17 +136,18 @@ fn build_mod_details() -> impl Widget<State> {
|
||||||
if *enabled {
|
if *enabled {
|
||||||
"Disable Mod".into()
|
"Disable Mod".into()
|
||||||
} else {
|
} else {
|
||||||
"Enabled Mod".into()
|
"Enable Mod".into()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on_click(|_ctx, info: &mut bool, _env| {
|
.on_click(|_ctx, enabled: &mut bool, _env| {
|
||||||
*info = !*info;
|
*enabled = !(*enabled);
|
||||||
})
|
})
|
||||||
.lens(ModInfo::enabled)
|
.lens(ModInfo::enabled)
|
||||||
},
|
},
|
||||||
// TODO: Gray out
|
// TODO: Gray out
|
||||||
|| Button::new("Enable Mod"),
|
|| Button::new("Enable Mod"),
|
||||||
)
|
)
|
||||||
|
.disabled_if(|info: &Option<ModInfo>, _env: &druid::Env| info.is_none())
|
||||||
.lens(State::selected_mod);
|
.lens(State::selected_mod);
|
||||||
|
|
||||||
let button_add_mod = Button::new("Add Mod").on_click(|_ctx, state: &mut State, _env| {
|
let button_add_mod = Button::new("Add Mod").on_click(|_ctx, state: &mut State, _env| {
|
||||||
|
@ -165,7 +157,9 @@ fn build_mod_details() -> impl Widget<State> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let button_delete_mod = Button::new("Delete Mod")
|
let button_delete_mod = Button::new("Delete Mod")
|
||||||
.on_click(|_ctx, data: &mut State, _env| data.delete_selected_mod());
|
.on_click(|ctx, _state, _env| ctx.submit_notification(ACTION_DELETE_SELECTED_MOD))
|
||||||
|
.disabled_if(|info: &Option<ModInfo>, _env: &druid::Env| info.is_none())
|
||||||
|
.lens(State::selected_mod);
|
||||||
|
|
||||||
let buttons = Flex::column()
|
let buttons = Flex::column()
|
||||||
.with_child(
|
.with_child(
|
||||||
|
@ -234,4 +228,5 @@ fn build_window() -> impl Widget<State> {
|
||||||
.must_fill_main_axis(true)
|
.must_fill_main_axis(true)
|
||||||
.with_child(build_top_bar())
|
.with_child(build_top_bar())
|
||||||
.with_flex_child(build_main(), 1.0)
|
.with_flex_child(build_main(), 1.0)
|
||||||
|
.controller(StateController::new())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use druid::im::Vector;
|
use druid::im::Vector;
|
||||||
use druid::{Data, Lens};
|
use druid::widget::Controller;
|
||||||
|
use druid::{Data, Env, Event, EventCtx, Lens, Selector, Widget};
|
||||||
|
|
||||||
|
pub const ACTION_SELECT_MOD: Selector<usize> = Selector::new("dtmm.action..select-mod");
|
||||||
|
pub const ACTION_SELECTED_MOD_UP: Selector = Selector::new("dtmm.action.selected-mod-up");
|
||||||
|
pub const ACTION_SELECTED_MOD_DOWN: Selector = Selector::new("dtmm.action.selected-mod-down");
|
||||||
|
pub const ACTION_DELETE_SELECTED_MOD: Selector = Selector::new("dtmm.action.delete-selected-mod");
|
||||||
|
|
||||||
#[derive(Copy, Clone, Data, PartialEq)]
|
#[derive(Copy, Clone, Data, PartialEq)]
|
||||||
pub(crate) enum View {
|
pub(crate) enum View {
|
||||||
|
@ -48,6 +54,7 @@ pub(crate) struct State {
|
||||||
pub(crate) struct SelectedModLens;
|
pub(crate) struct SelectedModLens;
|
||||||
|
|
||||||
impl Lens<State, Option<ModInfo>> for SelectedModLens {
|
impl Lens<State, Option<ModInfo>> for SelectedModLens {
|
||||||
|
#[tracing::instrument(name = "SelectedModLens::with", skip_all)]
|
||||||
fn with<V, F: FnOnce(&Option<ModInfo>) -> V>(&self, data: &State, f: F) -> V {
|
fn with<V, F: FnOnce(&Option<ModInfo>) -> V>(&self, data: &State, f: F) -> V {
|
||||||
let info = data
|
let info = data
|
||||||
.selected_mod_index
|
.selected_mod_index
|
||||||
|
@ -56,11 +63,25 @@ impl Lens<State, Option<ModInfo>> for SelectedModLens {
|
||||||
f(&info)
|
f(&info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "SelectedModLens::with_mut", skip_all)]
|
||||||
fn with_mut<V, F: FnOnce(&mut Option<ModInfo>) -> V>(&self, data: &mut State, f: F) -> V {
|
fn with_mut<V, F: FnOnce(&mut Option<ModInfo>) -> V>(&self, data: &mut State, f: F) -> V {
|
||||||
let mut info = data
|
match data.selected_mod_index {
|
||||||
.selected_mod_index
|
Some(i) => {
|
||||||
.and_then(|i| data.mods.get_mut(i).cloned());
|
let mut info = data.mods.get_mut(i).cloned();
|
||||||
f(&mut info)
|
let ret = f(&mut info);
|
||||||
|
|
||||||
|
if let Some(info) = info {
|
||||||
|
// TODO: Figure out a way to check for equality and
|
||||||
|
// only update when needed
|
||||||
|
data.mods.set(i, info);
|
||||||
|
} else {
|
||||||
|
data.selected_mod_index = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
None => f(&mut None),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,21 +91,33 @@ impl Lens<State, Option<ModInfo>> for SelectedModLens {
|
||||||
pub(crate) struct IndexedVectorLens;
|
pub(crate) struct IndexedVectorLens;
|
||||||
|
|
||||||
impl<T: Data> Lens<Vector<T>, Vector<(usize, T)>> for IndexedVectorLens {
|
impl<T: Data> Lens<Vector<T>, Vector<(usize, T)>> for IndexedVectorLens {
|
||||||
fn with<V, F: FnOnce(&Vector<(usize, T)>) -> V>(&self, data: &Vector<T>, f: F) -> V {
|
#[tracing::instrument(name = "IndexedVectorLens::with", skip_all)]
|
||||||
let data = data
|
fn with<V, F: FnOnce(&Vector<(usize, T)>) -> V>(&self, values: &Vector<T>, f: F) -> V {
|
||||||
|
let indexed = values
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, val)| (i, val.clone()))
|
.map(|(i, val)| (i, val.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
f(&data)
|
f(&indexed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "IndexedVectorLens::with_mut", skip_all)]
|
||||||
fn with_mut<V, F: FnOnce(&mut Vector<(usize, T)>) -> V>(
|
fn with_mut<V, F: FnOnce(&mut Vector<(usize, T)>) -> V>(
|
||||||
&self,
|
&self,
|
||||||
data: &mut Vector<T>,
|
values: &mut Vector<T>,
|
||||||
f: F,
|
f: F,
|
||||||
) -> V {
|
) -> V {
|
||||||
todo!()
|
let mut indexed = values
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, val)| (i, val.clone()))
|
||||||
|
.collect();
|
||||||
|
let ret = f(&mut indexed);
|
||||||
|
tracing::trace!("with_mut: {}", indexed.len());
|
||||||
|
|
||||||
|
*values = indexed.into_iter().map(|(_i, val)| val).collect();
|
||||||
|
|
||||||
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,16 +137,90 @@ impl State {
|
||||||
self.current_view = view;
|
self.current_view = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_selected_mod(&mut self) {
|
pub fn select_mod(&mut self, index: usize) {
|
||||||
let Some(index) = self.selected_mod_index else {
|
self.selected_mod_index = Some(index);
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
self.mods.remove(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_mod(&mut self, info: ModInfo) {
|
pub fn add_mod(&mut self, info: ModInfo) {
|
||||||
self.mods.push_back(info);
|
self.mods.push_back(info);
|
||||||
self.selected_mod_index = Some(self.mods.len() - 1);
|
self.selected_mod_index = Some(self.mods.len() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn can_move_mod_down(&self) -> bool {
|
||||||
|
self.selected_mod_index
|
||||||
|
.map(|i| i >= (self.mods.len().saturating_sub(1)))
|
||||||
|
.unwrap_or(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn can_move_mod_up(&self) -> bool {
|
||||||
|
self.selected_mod_index.map(|i| i == 0).unwrap_or(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StateController {}
|
||||||
|
|
||||||
|
impl StateController {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W: Widget<State>> Controller<State, W> for StateController {
|
||||||
|
#[tracing::instrument(name = "StateController::event", skip_all)]
|
||||||
|
fn event(
|
||||||
|
&mut self,
|
||||||
|
child: &mut W,
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
event: &Event,
|
||||||
|
state: &mut State,
|
||||||
|
env: &Env,
|
||||||
|
) {
|
||||||
|
match event {
|
||||||
|
Event::Notification(notif) if notif.is(ACTION_SELECT_MOD) => {
|
||||||
|
ctx.set_handled();
|
||||||
|
let index = notif
|
||||||
|
.get(ACTION_SELECT_MOD)
|
||||||
|
.expect("notification type didn't match after check");
|
||||||
|
|
||||||
|
state.select_mod(*index);
|
||||||
|
}
|
||||||
|
Event::Notification(notif) if notif.is(ACTION_SELECTED_MOD_UP) => {
|
||||||
|
ctx.set_handled();
|
||||||
|
let Some(i) = state.selected_mod_index else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = state.mods.len();
|
||||||
|
if len == 0 || i == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.mods.swap(i, i - 1);
|
||||||
|
state.selected_mod_index = Some(i - 1);
|
||||||
|
}
|
||||||
|
Event::Notification(notif) if notif.is(ACTION_SELECTED_MOD_DOWN) => {
|
||||||
|
ctx.set_handled();
|
||||||
|
let Some(i) = state.selected_mod_index else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = state.mods.len();
|
||||||
|
if len == 0 || i == usize::MAX || i >= len - 1 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.mods.swap(i, i + 1);
|
||||||
|
state.selected_mod_index = Some(i + 1);
|
||||||
|
}
|
||||||
|
Event::Notification(notif) if notif.is(ACTION_DELETE_SELECTED_MOD) => {
|
||||||
|
ctx.set_handled();
|
||||||
|
let Some(index) = state.selected_mod_index else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.mods.remove(index);
|
||||||
|
}
|
||||||
|
_ => child.event(ctx, event, state, env),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
60
crates/dtmm/src/widget/hidden_if.rs
Normal file
60
crates/dtmm/src/widget/hidden_if.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use druid::widget::prelude::*;
|
||||||
|
use druid::{Point, WidgetPod};
|
||||||
|
|
||||||
|
pub struct HiddenIf<T, W> {
|
||||||
|
child: WidgetPod<T, W>,
|
||||||
|
hidden_if: Box<dyn Fn(&T, &Env) -> bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data, W: Widget<T>> HiddenIf<T, W> {
|
||||||
|
pub fn new(child: W, hidden_if: impl Fn(&T, &Env) -> bool + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
hidden_if: Box::new(hidden_if),
|
||||||
|
child: WidgetPod::new(child),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data, W: Widget<T>> Widget<T> for HiddenIf<T, W> {
|
||||||
|
#[tracing::instrument(name = "HideContainer", level = "trace", skip_all)]
|
||||||
|
fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
|
||||||
|
let hidden = (self.hidden_if)(data, env);
|
||||||
|
ctx.set_disabled(hidden);
|
||||||
|
self.child.event(ctx, event, data, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "HideContainer", level = "trace", skip_all)]
|
||||||
|
fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
|
||||||
|
let hidden = (self.hidden_if)(data, env);
|
||||||
|
ctx.set_disabled(hidden);
|
||||||
|
self.child.lifecycle(ctx, event, data, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "HideContainer", level = "trace", skip_all)]
|
||||||
|
fn update(&mut self, ctx: &mut UpdateCtx, _: &T, data: &T, env: &Env) {
|
||||||
|
self.child.update(ctx, data, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "HideContainer", level = "trace", skip_all)]
|
||||||
|
fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
|
||||||
|
bc.debug_check("HideContainer");
|
||||||
|
let hidden = (self.hidden_if)(data, env);
|
||||||
|
if hidden {
|
||||||
|
return Size::ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
|
let child_size = self.child.layout(ctx, bc, data, env);
|
||||||
|
self.child.set_origin(ctx, Point::new(0.0, 0.0));
|
||||||
|
child_size
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(name = "HideContainer", level = "trace", skip_all)]
|
||||||
|
fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
|
||||||
|
let hidden = (self.hidden_if)(data, env);
|
||||||
|
if hidden {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.child.paint(ctx, data, env);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,20 @@
|
||||||
use druid::{Data, Widget};
|
use druid::{Data, Env, Widget};
|
||||||
|
|
||||||
use self::fill_container::FillContainer;
|
use self::fill_container::FillContainer;
|
||||||
|
use self::hidden_if::HiddenIf;
|
||||||
|
|
||||||
pub mod container;
|
pub mod container;
|
||||||
pub mod fill_container;
|
pub mod fill_container;
|
||||||
|
pub mod hidden_if;
|
||||||
|
|
||||||
pub trait ExtraWidgetExt<T: Data>: Widget<T> + Sized + 'static {
|
pub trait ExtraWidgetExt<T: Data>: Widget<T> + Sized + 'static {
|
||||||
fn content_must_fill(self) -> FillContainer<T> {
|
fn content_must_fill(self) -> FillContainer<T> {
|
||||||
FillContainer::new(self)
|
FillContainer::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn hidden_if(self, hidden_if: impl Fn(&T, &Env) -> bool + 'static) -> HiddenIf<T, Self> {
|
||||||
|
HiddenIf::new(self, hidden_if)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Data, W: Widget<T> + 'static> ExtraWidgetExt<T> for W {}
|
impl<T: Data, W: Widget<T> + 'static> ExtraWidgetExt<T> for W {}
|
||||||
|
|
73
crates/dtmm/src/widget/table_select.rs
Normal file
73
crates/dtmm/src/widget/table_select.rs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
use druid::widget::{Controller, Flex};
|
||||||
|
use druid::{Data, Widget};
|
||||||
|
|
||||||
|
pub struct TableSelect<T> {
|
||||||
|
widget: Flex<T>,
|
||||||
|
controller: TableSelectController<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data> TableSelect<T> {
|
||||||
|
pub fn new(values: impl IntoIterator<Item = (impl Widget<T> + 'static)>) -> Self {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data> Widget<T> for TableSelect<T> {
|
||||||
|
fn event(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut druid::EventCtx,
|
||||||
|
event: &druid::Event,
|
||||||
|
data: &mut T,
|
||||||
|
env: &druid::Env,
|
||||||
|
) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lifecycle(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut druid::LifeCycleCtx,
|
||||||
|
event: &druid::LifeCycle,
|
||||||
|
data: &T,
|
||||||
|
env: &druid::Env,
|
||||||
|
) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &mut druid::UpdateCtx, old_data: &T, data: &T, env: &druid::Env) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut druid::LayoutCtx,
|
||||||
|
bc: &druid::BoxConstraints,
|
||||||
|
data: &T,
|
||||||
|
env: &druid::Env,
|
||||||
|
) -> druid::Size {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self, ctx: &mut druid::PaintCtx, data: &T, env: &druid::Env) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TableSelectController<T> {
|
||||||
|
inner: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data> TableSelectController<T> {}
|
||||||
|
|
||||||
|
impl<T: Data> Controller<T, Flex<T>> for TableSelectController<T> {}
|
||||||
|
|
||||||
|
pub struct TableItem<T> {
|
||||||
|
inner: dyn Widget<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data> TableItem<T> {
|
||||||
|
pub fn new(inner: impl Widget<T>) -> Self {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Data> Widget<T> for TableItem<T> {}
|
Loading…
Add table
Reference in a new issue