Skip to content

Commit

Permalink
Add kas::theme::SelectionStyle
Browse files Browse the repository at this point in the history
  • Loading branch information
dhardy committed Feb 25, 2023
1 parent 213790b commit 6229840
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 28 deletions.
22 changes: 9 additions & 13 deletions crates/kas-core/src/theme/draw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

//! "Handle" types used by themes

use super::{FrameStyle, MarkStyle, SizeMgr, TextClass, ThemeSize};
use super::{FrameStyle, MarkStyle, SelectionStyle, SizeMgr, TextClass, ThemeSize};
use crate::dir::Direction;
use crate::draw::color::Rgb;
use crate::draw::{Draw, DrawIface, DrawShared, DrawSharedImpl, ImageId, PassType};
Expand Down Expand Up @@ -194,13 +194,13 @@ impl<'a> DrawMgr<'a> {
self.h.separator(rect);
}

/// Draw a selection box
/// Draw a selection highlight / frame
///
/// This appears as a dashed box or similar around this `rect`. Note that
/// the selection indicator is drawn *outside* of this rect, within a margin
/// of size [`SizeMgr::inner_margins`] that is expected to be present around this box.
pub fn selection_box(&mut self, rect: Rect) {
self.h.selection_box(rect);
/// Adjusts the background color and/or draws a line around the given rect.
/// In the latter case, a margin of size [`SizeMgr::inner_margins`] around
/// `rect` is expected.
pub fn selection(&mut self, rect: Rect, style: SelectionStyle) {
self.h.selection(rect, style);
}

/// Draw text
Expand Down Expand Up @@ -412,12 +412,8 @@ pub trait ThemeDraw {
/// Draw a separator in the given `rect`
fn separator(&mut self, rect: Rect);

/// Draw a selection box
///
/// This appears as a dashed box or similar around this `rect`. Note that
/// the selection indicator is drawn *outside* of this rect, within a margin
/// of size `inner_margin` that is expected to be present around this box.
fn selection_box(&mut self, rect: Rect);
/// Draw a selection highlight / frame
fn selection(&mut self, rect: Rect, style: SelectionStyle);

/// Draw text
///
Expand Down
24 changes: 18 additions & 6 deletions crates/kas-core/src/theme/simple_theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use kas::text::{fonts, Effect, TextApi, TextDisplay};
use kas::theme::dimensions as dim;
use kas::theme::{Background, FrameStyle, MarkStyle, TextClass};
use kas::theme::{ColorsLinear, Config, InputState, Theme};
use kas::theme::{ThemeControl, ThemeDraw, ThemeSize};
use kas::theme::{SelectionStyle, ThemeControl, ThemeDraw, ThemeSize};
use kas::{Action, WidgetId};

/// A simple theme
Expand Down Expand Up @@ -362,12 +362,24 @@ where
self.draw.rect(outer, self.cols.frame);
}

fn selection_box(&mut self, rect: Rect) {
fn selection(&mut self, rect: Rect, style: SelectionStyle) {
let inner = Quad::conv(rect);
let outer = inner.grow(self.w.dims.m_inner.into());
// TODO: this should use its own colour and a stippled pattern
let col = self.cols.text_sel_bg;
self.draw.frame(outer, inner, col);
match style {
SelectionStyle::Highlight => {
self.draw.rect(inner, self.cols.text_sel_bg);
}
SelectionStyle::Frame => {
let outer = inner.grow(self.w.dims.m_inner.into());
// TODO: this should use its own colour and a stippled pattern
let col = self.cols.accent;
self.draw.frame(outer, inner, col);
}
SelectionStyle::Both => {
let outer = inner.grow(self.w.dims.m_inner.into());
self.draw.rect(outer, self.cols.accent);
self.draw.rect(inner, self.cols.text_sel_bg);
}
}
}

fn text(&mut self, id: &WidgetId, rect: Rect, text: &TextDisplay, _: TextClass) {
Expand Down
22 changes: 22 additions & 0 deletions crates/kas-core/src/theme/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,28 @@ pub enum FrameStyle {
Window,
}

/// Selection style hint
///
/// How to draw selections
#[derive(Copy, Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum SelectionStyle {
/// Adjust background color
Highlight,
/// Draw a frame around the selection
Frame,
/// Both
Both,
}

impl SelectionStyle {
/// True if an external margin is required
///
/// Margin size: [`SizeMgr::inner_margins`]
pub fn is_external(self) -> bool {
matches!(self, SelectionStyle::Frame | SelectionStyle::Both)
}
}

/// Class of text drawn
///
/// Themes choose font, font size, colour, and alignment based on this.
Expand Down
6 changes: 3 additions & 3 deletions crates/kas-macros/src/extends.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ impl Extends {
(#base).get_clip_rect()
}

fn frame(&mut self, id: &WidgetId, rect: Rect, style: FrameStyle, bg: Background) {
fn frame(&mut self, id: &WidgetId, rect: Rect, style: ::kas::theme::FrameStyle, bg: Background) {
(#base).frame(id, rect, style, bg);
}

fn separator(&mut self, rect: Rect) {
(#base).separator(rect);
}

fn selection_box(&mut self, rect: Rect) {
(#base).selection_box(rect);
fn selection(&mut self, rect: Rect, style: ::kas::theme::SelectionStyle) {
(#base).selection(rect, style);
}

fn text(&mut self, id: &WidgetId, rect: Rect, text: &TextDisplay, class: TextClass) {
Expand Down
39 changes: 36 additions & 3 deletions crates/kas-view/src/list_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use kas::layout::{solve_size_rules, AlignHints};
#[allow(unused)] use kas::model::SharedData;
use kas::model::{ListData, SharedDataMut};
use kas::prelude::*;
use kas::theme::SelectionStyle;
#[allow(unused)] // doc links
use kas_widgets::ScrollBars;
use kas_widgets::SelectMsg;
Expand Down Expand Up @@ -72,6 +73,7 @@ impl_scope! {
child_size: Size,
scroll: ScrollComponent,
sel_mode: SelectionMode,
sel_style: SelectionStyle,
// TODO(opt): replace selection list with RangeOrSet type?
selection: LinearSet<T::Key>,
press_target: Option<(usize, T::Key)>,
Expand Down Expand Up @@ -139,6 +141,7 @@ impl_scope! {
child_size: Size::ZERO,
scroll: Default::default(),
sel_mode: SelectionMode::None,
sel_style: SelectionStyle::Highlight,
selection: Default::default(),
press_target: None,
}
Expand Down Expand Up @@ -236,6 +239,32 @@ impl_scope! {
self
}

/// Get the current selection style
pub fn selection_style(&self) -> SelectionStyle {
self.sel_style
}
/// Set the current selection style
///
/// By default, [`SelectionStyle::Highlight`] is used. Other modes may
/// add margin between elements.
pub fn set_selection_style(&mut self, style: SelectionStyle) -> Action {
let action = if style.is_external() != self.sel_style.is_external() {
Action::RESIZE
} else {
Action::empty()
};
self.sel_style = style;
action
}
/// Set the selection style (inline)
///
/// See [`Self::set_selection_style`] documentation.
#[must_use]
pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
self.sel_style = style;
self
}

/// Read the list of selected entries
///
/// With mode [`SelectionMode::Single`] this may contain zero or one entry;
Expand Down Expand Up @@ -473,7 +502,11 @@ impl_scope! {
impl Layout for Self {
fn size_rules(&mut self, size_mgr: SizeMgr, mut axis: AxisInfo) -> SizeRules {
// We use an invisible frame for highlighting selections, drawing into the margin
let inner_margin = size_mgr.inner_margins().extract(axis);
let inner_margin = if self.sel_style.is_external() {
size_mgr.inner_margins().extract(axis)
} else {
(0, 0)
};
let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));

let other = axis.other().map(|mut size| {
Expand Down Expand Up @@ -579,12 +612,12 @@ impl_scope! {
let offset = self.scroll_offset();
draw.with_clip_region(self.core.rect, offset, |mut draw| {
for child in &mut self.widgets[..self.cur_len.cast()] {
draw.recurse(&mut child.widget);
if let Some(ref key) = child.key {
if self.selection.contains(key) {
draw.selection_box(child.widget.rect());
draw.selection(child.widget.rect(), self.sel_style);
}
}
draw.recurse(&mut child.widget);
}
});
}
Expand Down
39 changes: 36 additions & 3 deletions crates/kas-view/src/matrix_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use kas::layout::{solve_size_rules, AlignHints};
#[allow(unused)] use kas::model::SharedData;
use kas::model::{MatrixData, SharedDataMut};
use kas::prelude::*;
use kas::theme::SelectionStyle;
#[allow(unused)] // doc links
use kas_widgets::ScrollBars;
use kas_widgets::SelectMsg;
Expand Down Expand Up @@ -78,6 +79,7 @@ impl_scope! {
child_size: Size,
scroll: ScrollComponent,
sel_mode: SelectionMode,
sel_style: SelectionStyle,
// TODO(opt): replace selection list with RangeOrSet type?
selection: LinearSet<T::Key>,
press_target: Option<(usize, T::Key)>,
Expand Down Expand Up @@ -115,6 +117,7 @@ impl_scope! {
child_size: Size::ZERO,
scroll: Default::default(),
sel_mode: SelectionMode::None,
sel_style: SelectionStyle::Highlight,
selection: Default::default(),
press_target: None,
}
Expand Down Expand Up @@ -212,6 +215,32 @@ impl_scope! {
self
}

/// Get the current selection style
pub fn selection_style(&self) -> SelectionStyle {
self.sel_style
}
/// Set the current selection style
///
/// By default, [`SelectionStyle::Highlight`] is used. Other modes may
/// add margin between elements.
pub fn set_selection_style(&mut self, style: SelectionStyle) -> Action {
let action = if style.is_external() != self.sel_style.is_external() {
Action::RESIZE
} else {
Action::empty()
};
self.sel_style = style;
action
}
/// Set the selection style (inline)
///
/// See [`Self::set_selection_style`] documentation.
#[must_use]
pub fn with_selection_style(mut self, style: SelectionStyle) -> Self {
self.sel_style = style;
self
}

/// Read the list of selected entries
///
/// With mode [`SelectionMode::Single`] this may contain zero or one entry;
Expand Down Expand Up @@ -451,7 +480,11 @@ impl_scope! {
impl Layout for Self {
fn size_rules(&mut self, size_mgr: SizeMgr, mut axis: AxisInfo) -> SizeRules {
// We use an invisible frame for highlighting selections, drawing into the margin
let inner_margin = size_mgr.inner_margins().extract(axis);
let inner_margin = if self.sel_style.is_external() {
size_mgr.inner_margins().extract(axis)
} else {
(0, 0)
};
let frame = kas::layout::FrameRules::new(0, inner_margin, (0, 0));

let other = axis.other().map(|mut size| {
Expand Down Expand Up @@ -569,10 +602,10 @@ impl_scope! {
// visible, so check intersection before drawing:
if rect.intersection(&child.widget.rect()).is_some() {
if let Some(ref key) = child.key {
draw.recurse(&mut child.widget);
if self.selection.contains(key) {
draw.selection_box(child.widget.rect());
draw.selection(child.widget.rect(), self.sel_style);
}
draw.recurse(&mut child.widget);
}
}
}
Expand Down

0 comments on commit 6229840

Please sign in to comment.