use std::collections::BTreeSet;
use egui::{
Button, Context, Frame, Id, InnerResponse, Layout, RichText, ScrollArea, UiBuilder, Widget,
};
use graphannis::graph::AnnoKey;
use serde::{Deserialize, Serialize};
pub struct FilterWidget {
available_node_annos: BTreeSet<AnnoKey>,
widget_id: Id,
}
#[derive(Clone, Default, Debug, Serialize, Deserialize)]
pub struct FilterWidgetState {
pub selected_node_annos: BTreeSet<AnnoKey>,
}
#[derive(Default, Debug)]
pub struct FilterWidgetOutput {
pub selected_node_annos: BTreeSet<AnnoKey>,
}
impl FilterWidget {
pub fn new<I: Into<Id>>(available_node_annos: BTreeSet<AnnoKey>, widget_id: I) -> Self {
Self {
available_node_annos,
widget_id: widget_id.into(),
}
}
pub fn load_state(&self, ctx: &Context) -> FilterWidgetState {
ctx.data_mut(|d| d.get_persisted(self.widget_id))
.unwrap_or_default()
}
pub fn store_state(&self, ctx: &Context, state: FilterWidgetState) {
ctx.data_mut(|d| d.insert_persisted(self.widget_id, state));
}
pub fn show(self, ui: &mut egui::Ui) -> InnerResponse<FilterWidgetOutput> {
let mut state = self.load_state(ui.ctx());
let style = ui.style().clone();
let response = Frame::group(&style)
.show(ui, |ui| {
ui.vertical(|ui| {
let header_builder =
UiBuilder::new().layout(Layout::left_to_right(egui::Align::Min));
ui.scope_builder(header_builder, |ui| {
ui.label(RichText::new("Show spans").underline());
let mut all_button = Button::new("All");
if !self.available_node_annos.is_empty()
&& state
.selected_node_annos
.is_superset(&self.available_node_annos)
{
all_button = all_button.fill(style.visuals.selection.bg_fill);
}
if all_button.ui(ui).clicked() {
state
.selected_node_annos
.extend(self.available_node_annos.clone());
}
let mut none_button = Button::new("None");
if !self.available_node_annos.is_empty()
&& state
.selected_node_annos
.is_disjoint(&self.available_node_annos)
{
none_button = none_button.fill(style.visuals.selection.bg_fill);
}
if none_button.ui(ui).clicked() {
state.selected_node_annos.clear();
}
});
ScrollArea::vertical().show(ui, |ui| {
for key in &self.available_node_annos {
let is_visible = state.selected_node_annos.contains(key);
let displayed_icon = if is_visible {
egui_phosphor::regular::EYE
} else {
egui_phosphor::regular::EYE_SLASH
};
let button_color = if is_visible {
style.visuals.selection.bg_fill
} else {
style.visuals.widgets.active.bg_fill
};
let button = if key.ns.is_empty() {
Button::new((key.name.as_str(), displayed_icon)).fill(button_color)
} else {
Button::new((
RichText::new(key.ns.as_str()).small(),
key.name.as_str(),
displayed_icon,
))
.fill(button_color)
};
if button.ui(ui).clicked() {
if is_visible {
state.selected_node_annos.remove(key);
} else {
state.selected_node_annos.insert(key.clone());
}
}
}
});
})
})
.response;
let output = FilterWidgetOutput {
selected_node_annos: state.selected_node_annos.clone(),
};
self.store_state(ui.ctx(), state);
InnerResponse::new(output, response)
}
pub fn select_anno_key(&mut self, anno_key: AnnoKey, ui: &egui::Ui) {
let mut state = self.load_state(ui.ctx());
state.selected_node_annos.insert(anno_key);
self.store_state(ui.ctx(), state);
}
}
impl Widget for FilterWidget {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
self.show(ui).response
}
}