use std::fmt::{self, Debug};
use super::Menu;
use kas::class::{HasBool, HasText};
use kas::draw::TextClass;
use kas::layout::{RulesSetter, RulesSolver};
use kas::prelude::*;
use kas::widget::{AccelLabel, CheckBoxBare};
#[widget(config=noauto)]
#[handler(handle=noauto)]
#[derive(Clone, Debug, Default, Widget)]
pub struct MenuEntry<M: Clone + Debug + 'static> {
#[widget_core]
core: kas::CoreData,
label: AccelString,
label_off: Coord,
msg: M,
}
impl<M: Clone + Debug + 'static> WidgetConfig for MenuEntry<M> {
fn configure(&mut self, mgr: &mut Manager) {
mgr.add_accel_keys(self.id(), self.label.keys());
}
fn key_nav(&self) -> bool {
true
}
}
impl<M: Clone + Debug + 'static> Layout for MenuEntry<M> {
fn size_rules(&mut self, size_handle: &mut dyn SizeHandle, axis: AxisInfo) -> SizeRules {
let size = size_handle.menu_frame();
self.label_off = size.into();
let frame_rules = SizeRules::extract_fixed(axis.is_vertical(), size + size, Margins::ZERO);
let text_rules = size_handle.text_bound(self.label.get(false), TextClass::Label, axis);
text_rules.surrounded_by(frame_rules, true)
}
fn draw(&self, draw_handle: &mut dyn DrawHandle, mgr: &event::ManagerState, disabled: bool) {
draw_handle.menu_entry(self.core.rect, self.input_state(mgr, disabled));
let rect = Rect {
pos: self.core.rect.pos + self.label_off,
size: self.core.rect.size - self.label_off.into(),
};
let text = self.label.get(mgr.show_accel_labels());
let align = (Align::Begin, Align::Centre);
draw_handle.text(rect, text, TextClass::Label, align);
}
}
impl<M: Clone + Debug + 'static> MenuEntry<M> {
pub fn new<S: Into<AccelString>>(label: S, msg: M) -> Self {
MenuEntry {
core: Default::default(),
label: label.into(),
label_off: Coord::ZERO,
msg,
}
}
pub fn set_msg(&mut self, msg: M) {
self.msg = msg;
}
}
impl<M: Clone + Debug + 'static> HasText for MenuEntry<M> {
fn get_text(&self) -> &str {
self.label.get(false)
}
fn set_cow_string(&mut self, text: CowString) -> TkAction {
self.label = text.into();
TkAction::Redraw
}
}
impl<M: Clone + Debug + 'static> event::Handler for MenuEntry<M> {
type Msg = M;
fn handle(&mut self, _: &mut Manager, event: Event) -> Response<M> {
match event {
Event::Activate => self.msg.clone().into(),
event => Response::Unhandled(event),
}
}
}
impl<M: Clone + Debug> Menu for MenuEntry<M> {}
#[handler(msg = M, generics = <> where M: From<VoidMsg>)]
#[widget(config=noauto)]
#[derive(Clone, Default, Widget)]
pub struct MenuToggle<M: 'static> {
#[widget_core]
core: CoreData,
layout_data: layout::FixedRowStorage<[SizeRules; 3], [u32; 2]>,
#[widget]
checkbox: CheckBoxBare<M>,
#[widget]
label: AccelLabel,
}
impl<M: 'static> Debug for MenuToggle<M> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"MenuToggle {{ core: {:?}, layout_data: {:?}, checkbox: {:?}, label: {:?} }}",
self.core, self.layout_data, self.checkbox, self.label,
)
}
}
impl<M: 'static> MenuToggle<M> {
#[inline]
pub fn new_on<T: Into<AccelString>, F>(f: F, label: T) -> Self
where
F: Fn(bool) -> M + 'static,
{
MenuToggle {
core: Default::default(),
layout_data: Default::default(),
checkbox: CheckBoxBare::new_on(f),
label: AccelLabel::new(label),
}
}
#[inline]
pub fn state(mut self, state: bool) -> Self {
self.checkbox = self.checkbox.state(state);
self
}
}
impl MenuToggle<VoidMsg> {
#[inline]
pub fn new<T: Into<AccelString>>(label: T) -> Self {
MenuToggle {
core: Default::default(),
layout_data: Default::default(),
checkbox: CheckBoxBare::new(),
label: AccelLabel::new(label),
}
}
#[inline]
pub fn on_toggle<M, F>(self, f: F) -> MenuToggle<M>
where
F: Fn(bool) -> M + 'static,
{
MenuToggle {
core: self.core,
layout_data: self.layout_data,
checkbox: self.checkbox.on_toggle(f),
label: self.label,
}
}
}
impl<M: 'static> WidgetConfig for MenuToggle<M> {
fn configure(&mut self, mgr: &mut Manager) {
mgr.add_accel_keys(self.checkbox.id(), self.label.keys());
}
}
impl<M: 'static> Layout for MenuToggle<M> {
fn size_rules(
&mut self,
size_handle: &mut dyn SizeHandle,
axis: AxisInfo,
) -> kas::layout::SizeRules {
let mut solver = layout::RowSolver::new(axis, (kas::Right, 2usize), &mut self.layout_data);
let child = &mut self.checkbox;
solver.for_child(&mut self.layout_data, 0usize, |axis| {
child.size_rules(size_handle, axis)
});
let child = &mut self.label;
solver.for_child(&mut self.layout_data, 1usize, |axis| {
child.size_rules(size_handle, axis)
});
solver.finish(&mut self.layout_data)
}
fn set_rect(&mut self, rect: Rect, align: AlignHints) {
self.core.rect = rect;
let mut setter = layout::RowSetter::<_, [u32; 2], _>::new(
rect,
(kas::Right, 2usize),
align,
&mut self.layout_data,
);
let align = kas::AlignHints::NONE;
self.checkbox.set_rect(
setter.child_rect(&mut self.layout_data, 0usize),
align.clone(),
);
self.label
.set_rect(setter.child_rect(&mut self.layout_data, 1usize), align);
}
fn find_id(&self, coord: Coord) -> Option<WidgetId> {
if !self.rect().contains(coord) {
return None;
}
Some(self.checkbox.id())
}
fn draw(&self, draw_handle: &mut dyn DrawHandle, mgr: &event::ManagerState, disabled: bool) {
let state = self.checkbox.input_state(mgr, disabled);
draw_handle.menu_entry(self.core.rect, state);
self.checkbox.draw(draw_handle, mgr, state.disabled);
self.label.draw(draw_handle, mgr, state.disabled);
}
}
impl<M: From<VoidMsg>> Menu for MenuToggle<M> {}
impl<M: 'static> HasBool for MenuToggle<M> {
#[inline]
fn get_bool(&self) -> bool {
self.checkbox.get_bool()
}
#[inline]
fn set_bool(&mut self, state: bool) -> TkAction {
self.checkbox.set_bool(state)
}
}