Skip to content

Commit

Permalink
[skip ci] Fix interactions and highlights
Browse files Browse the repository at this point in the history
  • Loading branch information
grtlr committed Nov 28, 2024
1 parent f8429ae commit f50d698
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 48 deletions.
61 changes: 56 additions & 5 deletions crates/viewer/re_space_view_graph/src/draw.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use egui::{
Align2, Color32, FontId, Painter, Pos2, Rect, Response, Sense, Shape, Stroke, Ui, UiBuilder,
Vec2,
Align2, Color32, FontId, Label, Painter, Pos2, Rect, Response, Sense, Shape, Stroke, Ui,
UiBuilder, Vec2,
};
use re_chunk::EntityPath;
use re_viewer_context::{InteractionHighlight, SpaceViewHighlights};
use re_viewer_context::{
HoverHighlight, InteractionHighlight, SelectionHighlight, SpaceViewHighlights,
};

use crate::ui::draw::DrawableLabel;
use crate::ui::draw::{CircleLabel, DrawableLabel, TextLabel};

// Sorry for the pun, could not resist 😎.
// On a serious note, is there no other way to create a `Sense` that does nothing?
Expand All @@ -15,6 +17,49 @@ const NON_SENSE: Sense = Sense {
focusable: false,
};

fn draw_circle_label(
ui: &mut Ui,
label: &CircleLabel,
_highlight: InteractionHighlight,
) -> Response {
let &CircleLabel { radius, color } = label;
let (resp, painter) = ui.allocate_painter(Vec2::splat(radius * 2.0), Sense::click());
painter.circle(
resp.rect.center(),
radius,
color.unwrap_or_else(|| ui.style().visuals.text_color()),
Stroke::NONE,
);
resp
}

fn draw_text_label(ui: &mut Ui, label: &TextLabel, highlight: InteractionHighlight) -> Response {
let TextLabel { galley, frame } = label;
let visuals = &ui.style().visuals;

let bg = match highlight.hover {
HoverHighlight::None => visuals.widgets.noninteractive.bg_fill,
HoverHighlight::Hovered => visuals.widgets.hovered.bg_fill,
};

let stroke = match highlight.selection {
SelectionHighlight::Selection => visuals.selection.stroke,
_ => Stroke::new(1.0, visuals.text_color()),
};

frame
.stroke(stroke)
.fill(bg)
.show(ui, |ui| {
ui.add(
Label::new(galley.clone())
.selectable(false)
.sense(Sense::click()),
)
})
.inner
}

/// Draws a node at the given position.
pub fn draw_node(
ui: &mut Ui,
Expand All @@ -24,7 +69,13 @@ pub fn draw_node(
) -> Response {
let builder = UiBuilder::new().max_rect(Rect::from_center_size(center, node.size()));
let mut node_ui = ui.new_child(builder);
node.draw(&mut node_ui, highlight)

// TODO(grtlr): handle highlights

match node {
DrawableLabel::Circle(label) => draw_circle_label(&mut node_ui, label, highlight),
DrawableLabel::Text(label) => draw_text_label(&mut node_ui, label, highlight),
}
}

/// Draws a bounding box, as well as a basic coordinate system.
Expand Down
2 changes: 1 addition & 1 deletion crates/viewer/re_space_view_graph/src/ui/draw/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod node;

pub use node::DrawableLabel;
pub use node::{CircleLabel, TextLabel, DrawableLabel};
46 changes: 9 additions & 37 deletions crates/viewer/re_space_view_graph/src/ui/draw/node.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::sync::Arc;

use egui::{
Color32, FontSelection, Frame, Galley, Pos2, Rect, Response, RichText, Sense, Stroke,
Color32, FontSelection, Frame, Galley, RichText, Stroke,
TextWrapMode, Ui, Vec2, WidgetText,
};
use re_types::ArrowString;
Expand All @@ -21,26 +21,26 @@ impl DrawableLabel {
pub fn from_label(ui: &Ui, label: &Label) -> Self {
match label {
&Label::Circle { radius, color } => Self::circle(radius, color),
Label::Text { text, color } => Self::text(ui, text.clone(), *color),
Label::Text { text, color } => Self::text(ui, text, *color),
}
}
}

pub struct TextLabel {
frame: Frame,
galley: Arc<Galley>,
pub frame: Frame,
pub galley: Arc<Galley>,
}

pub struct CircleLabel {
radius: f32,
color: Option<Color32>,
pub radius: f32,
pub color: Option<Color32>,
}

impl DrawableLabel {
pub fn size(&self) -> Vec2 {
match self {
DrawableLabel::Circle(CircleLabel { radius, .. }) => Vec2::splat(radius * 2.0),
DrawableLabel::Text(TextLabel { galley, frame }) => {
Self::Circle(CircleLabel { radius, .. }) => Vec2::splat(radius * 2.0),
Self::Text(TextLabel { galley, frame }) => {
frame.inner_margin.sum() + galley.size() + Vec2::splat(frame.stroke.width * 2.0)
}
}
Expand All @@ -57,7 +57,7 @@ impl DrawableLabel {
})
}

pub fn text(ui: &Ui, text: ArrowString, color: Option<Color32>) -> Self {
pub fn text(ui: &Ui, text: &ArrowString, color: Option<Color32>) -> Self {
let galley = WidgetText::from(
RichText::new(text.to_string())
.color(color.unwrap_or_else(|| ui.style().visuals.text_color())),
Expand All @@ -76,34 +76,6 @@ impl DrawableLabel {

Self::Text(TextLabel { frame, galley })
}

pub fn draw(&self, ui: &mut Ui, highlight: InteractionHighlight) -> Response {
// TODO(grtlr): handle highlights
let sense = Sense::drag();
match self {
Self::Circle(CircleLabel { radius, color }) => {
let (resp, painter) = ui.allocate_painter(Vec2::splat(radius * 2.0), sense);
painter.circle(
resp.rect.center(),
*radius,
color.unwrap_or_else(|| ui.style().visuals.text_color()),
Stroke::NONE,
);
resp
}
Self::Text(TextLabel { galley, frame }) => {
frame
.show(ui, |ui| {
ui.add(
egui::Label::new(galley.clone())
.selectable(false)
.sense(sense),
)
})
.response
}
}
}
}

// /// The `world_to_ui_scale` parameter is used to convert between world and ui coordinates.
Expand Down
34 changes: 29 additions & 5 deletions crates/viewer/re_space_view_graph/src/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use re_viewport_blueprint::ViewProperty;

use crate::{
draw::{draw_debug, draw_edge, draw_entity_rect, draw_node},
graph::Graph,
graph::{Graph, Node},
layout::LayoutRequest,
ui::GraphSpaceViewState,
visualizers::{merge, EdgesVisualizer, NodeVisualizer},
Expand Down Expand Up @@ -176,15 +176,40 @@ Display a graph of nodes and edges.
let mut world_bounding_rect = egui::Rect::NOTHING;

for graph in &graphs {
let entity_path = graph.entity();
let entity_highlights = query.highlights.entity_highlight(entity_path.hash());

// For now we compute the entity rectangles on the fly.
let mut current_rect = egui::Rect::NOTHING;

for node in graph.nodes() {
let center = layout.get_node(&node.id()).center();

// TODO(grtlr): Add proper highlights here:
let resp = draw_node(ui, center, node.label(), Default::default());
current_rect = current_rect.union(resp.rect);
let response = match node {
Node::Explicit { instance, .. } => {
let highlight = entity_highlights.index_highlight(*instance);
let response =
draw_node(ui, center, node.label(), highlight);

let instance_path =
InstancePath::instance(entity_path.clone(), *instance);
ctx.select_hovered_on_click(
&response,
vec![(
Item::DataResult(query.space_view_id, instance_path),
None,
)]
.into_iter(),
);
response
}
Node::Implicit { .. } => {
draw_node(ui, center, node.label(), Default::default())
}
};

// TODO(grtlr): handle tooltips
current_rect = current_rect.union(response.rect);
}

for edge in graph.edges() {
Expand All @@ -195,7 +220,6 @@ Display a graph of nodes and edges.

// We only show entity rects if there are multiple entities.
if graphs.len() > 1 {
let entity_path = graph.entity();
let resp =
draw_entity_rect(ui, current_rect, entity_path, &query.highlights);

Expand Down

0 comments on commit f50d698

Please sign in to comment.