use super::{FrameStyle, MarkStyle, SizeMgr, TextClass, ThemeSize};
use crate::dir::Direction;
use crate::draw::color::Rgb;
use crate::draw::{Draw, DrawIface, DrawShared, DrawSharedImpl, ImageId, PassType};
use crate::event::{ConfigMgr, EventState};
use crate::geom::{Offset, Rect};
use crate::text::{TextApi, TextDisplay};
use crate::{autoimpl, TkAction, Widget, WidgetExt, WidgetId};
use std::convert::AsRef;
use std::ops::{Bound, Range, RangeBounds};
use std::time::Instant;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum Background {
Default,
Error,
Rgb(Rgb),
}
impl Default for Background {
fn default() -> Self {
Background::Default
}
}
pub struct DrawMgr<'a> {
h: &'a mut dyn ThemeDraw,
id: WidgetId,
}
impl<'a> DrawMgr<'a> {
#[inline(always)]
pub fn re_id<'b>(&'b mut self, id: WidgetId) -> DrawMgr<'b>
where
'a: 'b,
{
DrawMgr { h: self.h, id }
}
#[inline(always)]
pub fn re_clone<'b>(&'b mut self) -> DrawMgr<'b>
where
'a: 'b,
{
DrawMgr {
h: self.h,
id: self.id.clone(),
}
}
#[inline]
pub fn recurse(&mut self, child: &mut dyn Widget) {
child.draw(self.re_id(child.id()));
}
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
pub fn new(h: &'a mut dyn ThemeDraw, id: WidgetId) -> Self {
DrawMgr { h, id }
}
pub fn ev_state(&mut self) -> &EventState {
self.h.components().2
}
pub fn size_mgr(&mut self) -> SizeMgr {
SizeMgr::new(self.h.components().0)
}
pub fn config_mgr<F: FnMut(&mut ConfigMgr) -> T, T>(&mut self, mut f: F) -> T {
let (sh, draw, ev) = self.h.components();
let mut mgr = ConfigMgr::new(sh, draw.shared(), ev);
f(&mut mgr)
}
pub fn draw_shared(&mut self) -> &mut dyn DrawShared {
self.h.components().1.shared()
}
pub fn draw_device(&mut self) -> &mut dyn Draw {
self.h.components().1
}
pub fn draw_iface<DS: DrawSharedImpl>(&mut self) -> Option<DrawIface<DS>> {
DrawIface::downcast_from(self.draw_device())
}
pub fn with_pass<F: FnOnce(DrawMgr)>(&mut self, f: F) {
let clip_rect = self.h.get_clip_rect();
let id = self.id.clone();
self.h.new_pass(
clip_rect,
Offset::ZERO,
PassType::Clip,
Box::new(|h| f(DrawMgr { h, id })),
);
}
pub fn with_clip_region<F: FnOnce(DrawMgr)>(&mut self, rect: Rect, offset: Offset, f: F) {
let id = self.id.clone();
self.h.new_pass(
rect,
offset,
PassType::Clip,
Box::new(|h| f(DrawMgr { h, id })),
);
}
pub fn with_overlay<F: FnOnce(DrawMgr)>(&mut self, rect: Rect, f: F) {
let id = self.id.clone();
self.h.new_pass(
rect,
Offset::ZERO,
PassType::Overlay,
Box::new(|h| f(DrawMgr { h, id })),
);
}
pub fn get_clip_rect(&mut self) -> Rect {
self.h.get_clip_rect()
}
pub fn frame(&mut self, rect: Rect, style: FrameStyle, bg: Background) {
self.h.frame(&self.id, rect, style, bg)
}
pub fn separator(&mut self, rect: Rect) {
self.h.separator(rect);
}
pub fn selection_box(&mut self, rect: Rect) {
self.h.selection_box(rect);
}
pub fn text(&mut self, rect: Rect, text: impl AsRef<TextDisplay>, class: TextClass) {
self.h.text(&self.id, rect, text.as_ref(), class);
}
pub fn text_effects(&mut self, rect: Rect, text: &dyn TextApi, class: TextClass) {
self.h.text_effects(&self.id, rect, text, class);
}
pub fn text_selected<R: RangeBounds<usize>>(
&mut self,
rect: Rect,
text: impl AsRef<TextDisplay>,
range: R,
class: TextClass,
) {
let start = match range.start_bound() {
Bound::Included(n) => *n,
Bound::Excluded(n) => *n + 1,
Bound::Unbounded => 0,
};
let end = match range.end_bound() {
Bound::Included(n) => *n + 1,
Bound::Excluded(n) => *n,
Bound::Unbounded => usize::MAX,
};
let range = Range { start, end };
self.h
.text_selected_range(&self.id, rect, text.as_ref(), range, class);
}
pub fn text_cursor(
&mut self,
rect: Rect,
text: impl AsRef<TextDisplay>,
class: TextClass,
byte: usize,
) {
self.h
.text_cursor(&self.id, rect, text.as_ref(), class, byte);
}
pub fn check_box(&mut self, rect: Rect, checked: bool, last_change: Option<Instant>) {
self.h.check_box(&self.id, rect, checked, last_change);
}
pub fn radio_box(&mut self, rect: Rect, checked: bool, last_change: Option<Instant>) {
self.h.radio_box(&self.id, rect, checked, last_change);
}
pub fn mark(&mut self, rect: Rect, style: MarkStyle) {
self.h.mark(&self.id, rect, style);
}
pub fn scroll_bar<W: Widget>(&mut self, track_rect: Rect, handle: &W, dir: Direction) {
self.h
.scroll_bar(&self.id, handle.id_ref(), track_rect, handle.rect(), dir);
}
pub fn slider<W: Widget>(&mut self, track_rect: Rect, handle: &W, dir: Direction) {
self.h
.slider(&self.id, handle.id_ref(), track_rect, handle.rect(), dir);
}
pub fn progress_bar(&mut self, rect: Rect, dir: Direction, value: f32) {
self.h.progress_bar(&self.id, rect, dir, value);
}
pub fn image(&mut self, rect: Rect, id: ImageId) {
self.h.image(id, rect);
}
}
impl<'a> std::ops::BitOrAssign<TkAction> for DrawMgr<'a> {
#[inline]
fn bitor_assign(&mut self, action: TkAction) {
self.h.components().2.send_action(action);
}
}
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(doc_cfg, doc(cfg(internal_doc)))]
#[autoimpl(for<H: trait + ?Sized> Box<H>)]
pub trait ThemeDraw {
fn components(&mut self) -> (&dyn ThemeSize, &mut dyn Draw, &mut EventState);
fn new_pass<'a>(
&mut self,
rect: Rect,
offset: Offset,
class: PassType,
f: Box<dyn FnOnce(&mut dyn ThemeDraw) + 'a>,
);
fn get_clip_rect(&mut self) -> Rect;
fn frame(&mut self, id: &WidgetId, rect: Rect, style: FrameStyle, bg: Background);
fn separator(&mut self, rect: Rect);
fn selection_box(&mut self, rect: Rect);
fn text(&mut self, id: &WidgetId, rect: Rect, text: &TextDisplay, class: TextClass);
fn text_effects(&mut self, id: &WidgetId, rect: Rect, text: &dyn TextApi, class: TextClass);
fn text_selected_range(
&mut self,
id: &WidgetId,
rect: Rect,
text: &TextDisplay,
range: Range<usize>,
class: TextClass,
);
fn text_cursor(
&mut self,
id: &WidgetId,
rect: Rect,
text: &TextDisplay,
class: TextClass,
byte: usize,
);
fn check_box(&mut self, id: &WidgetId, rect: Rect, checked: bool, last_change: Option<Instant>);
fn radio_box(&mut self, id: &WidgetId, rect: Rect, checked: bool, last_change: Option<Instant>);
fn mark(&mut self, id: &WidgetId, rect: Rect, style: MarkStyle);
fn scroll_bar(
&mut self,
id: &WidgetId,
id2: &WidgetId,
rect: Rect,
h_rect: Rect,
dir: Direction,
);
fn slider(&mut self, id: &WidgetId, id2: &WidgetId, rect: Rect, h_rect: Rect, dir: Direction);
fn progress_bar(&mut self, id: &WidgetId, rect: Rect, dir: Direction, value: f32);
fn image(&mut self, id: ImageId, rect: Rect);
}
#[cfg(test)]
mod test {
use super::*;
fn _draw_ext(mut draw: DrawMgr) {
let _scale = draw.size_mgr().scale_factor();
let text = crate::text::Text::new("sample");
let class = TextClass::Label(false);
draw.text_selected(Rect::ZERO, &text, .., class)
}
}