Skip to content

Commit 390928f

Browse files
authored
Merge pull request #39 from dmackdev/truncation
support truncation and wrapping options
2 parents c9d23b3 + 6d73646 commit 390928f

File tree

7 files changed

+252
-25
lines changed

7 files changed

+252
-25
lines changed

examples/demo/src/apps/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ pub mod custom_input;
77
pub mod editor;
88
pub mod search;
99
pub mod toggle_buttons;
10+
pub mod wrapping;
1011

1112
pub trait Show {
1213
fn title(&self) -> &'static str;

examples/demo/src/apps/wrapping.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
use egui::{DragValue, Ui};
2+
use egui_json_tree::{
3+
DefaultExpand, JsonTree, JsonTreeMaxWidth, JsonTreeStyle, JsonTreeWrapping,
4+
JsonTreeWrappingConfig,
5+
};
6+
use serde_json::Value;
7+
8+
use super::Show;
9+
10+
pub struct WrappingExample {
11+
value: Value,
12+
wrap: JsonTreeWrapping,
13+
use_custom_max_rows: bool,
14+
}
15+
16+
impl WrappingExample {
17+
pub fn new(value: Value) -> Self {
18+
Self {
19+
value,
20+
wrap: JsonTreeWrapping {
21+
max_rows: 1,
22+
max_width: JsonTreeMaxWidth::UiAvailableWidth,
23+
break_anywhere: true,
24+
},
25+
use_custom_max_rows: true,
26+
}
27+
}
28+
}
29+
30+
impl Show for WrappingExample {
31+
fn title(&self) -> &'static str {
32+
"Wrapping"
33+
}
34+
35+
fn show(&mut self, ui: &mut Ui) {
36+
ui.hyperlink_to("Source", "https://github.com/dmackdev/egui_json_tree/blob/master/examples/demo/src/apps/wrapping.rs");
37+
ui.add_space(10.0);
38+
39+
self.show_max_rows_controls(ui);
40+
ui.add_space(10.0);
41+
42+
self.show_max_width_controls(ui);
43+
ui.add_space(10.0);
44+
45+
ui.checkbox(&mut self.wrap.break_anywhere, "Break anywhere");
46+
ui.separator();
47+
48+
let wrapping_config = JsonTreeWrappingConfig {
49+
value_when_root: self.wrap,
50+
value_with_expanded_parent: self.wrap,
51+
value_in_collapsed_root: self.wrap,
52+
};
53+
JsonTree::new(self.title(), &self.value)
54+
.style(JsonTreeStyle::new().wrapping_config(wrapping_config))
55+
.default_expand(DefaultExpand::All)
56+
.show(ui);
57+
}
58+
}
59+
60+
impl WrappingExample {
61+
fn show_max_rows_controls(&mut self, ui: &mut Ui) {
62+
ui.label(egui::RichText::new("Max Rows:").monospace());
63+
ui.horizontal(|ui| {
64+
if ui
65+
.radio_value(&mut self.use_custom_max_rows, true, "Custom")
66+
.changed()
67+
{
68+
self.wrap.max_rows = 1;
69+
}
70+
71+
if self.use_custom_max_rows {
72+
ui.add(
73+
DragValue::new(&mut self.wrap.max_rows)
74+
.speed(0.1)
75+
.range(1..=10),
76+
);
77+
}
78+
});
79+
80+
if ui
81+
.radio_value(&mut self.use_custom_max_rows, false, "usize::MAX")
82+
.clicked()
83+
{
84+
self.wrap.max_rows = usize::MAX;
85+
}
86+
}
87+
88+
fn show_max_width_controls(&mut self, ui: &mut Ui) {
89+
ui.label(egui::RichText::new("Max Width:").monospace());
90+
ui.horizontal(|ui| {
91+
if ui
92+
.radio(
93+
matches!(self.wrap.max_width, JsonTreeMaxWidth::Points(_)),
94+
"Points",
95+
)
96+
.clicked()
97+
&& !matches!(self.wrap.max_width, JsonTreeMaxWidth::Points(_))
98+
{
99+
self.wrap.max_width = JsonTreeMaxWidth::Points(100.0);
100+
}
101+
if let JsonTreeMaxWidth::Points(ref mut pts) = &mut self.wrap.max_width {
102+
ui.add(DragValue::new(pts).speed(10.0).range(100.0..=10000.0));
103+
}
104+
});
105+
106+
if ui
107+
.radio(
108+
matches!(self.wrap.max_width, JsonTreeMaxWidth::UiAvailableWidth),
109+
"Available Width",
110+
)
111+
.clicked()
112+
{
113+
self.wrap.max_width = JsonTreeMaxWidth::UiAvailableWidth;
114+
}
115+
}
116+
}

examples/demo/src/main.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use apps::{
22
copy_to_clipboard::CopyToClipboardExample, custom_input::CustomExample,
33
editor::JsonEditorExample, search::SearchExample,
4-
toggle_buttons::ToggleButtonsCustomisationDemo, Example, Show,
4+
toggle_buttons::ToggleButtonsCustomisationDemo, wrapping::WrappingExample, Example, Show,
55
};
66
use serde_json::json;
77

@@ -16,6 +16,20 @@ struct DemoApp {
1616
impl Default for DemoApp {
1717
fn default() -> Self {
1818
let complex_object = json!({"foo": [1, 2, [3]], "bar": { "qux" : false, "thud": { "a/b": [4, 5, { "m~n": "Greetings!" }]}, "grep": 21}, "baz": null});
19+
let long_strings_object = json!({
20+
"baz": "Ullamco ipsum proident occaecat eiusmod ea aute ex non cupidatat laboris duis amet cupidatat. Ullamco sint do enim consectetur Lorem occaecat mollit. Aliquip voluptate ullamco consectetur adipisicing elit fugiat labore laboris. Occaecat non incididunt duis consectetur aliquip dolore cillum eiusmod. Qui sunt est excepteur laborum.",
21+
"bar": [
22+
"Laboris id occaecat sit quis aliqua et. Fugiat nisi nulla nostrud voluptate id enim do esse deserunt non culpa incididunt eiusmod. Minim nulla reprehenderit irure duis amet commodo commodo aliquip ut. Lorem amet ipsum excepteur consectetur qui dolore. In occaecat dolor ullamco voluptate dolore qui incididunt occaecat pariatur est qui aliquip labore non.",
23+
"Velit ex nisi in et enim veniam ullamco reprehenderit consectetur Lorem. Dolor commodo pariatur Lorem proident. Ad minim aliquip excepteur officia consequat nulla mollit adipisicing ut veniam Lorem. Sint mollit occaecat velit do. Nulla aute Lorem non excepteur.",
24+
"Officia culpa in adipisicing sunt qui culpa voluptate ad veniam adipisicing anim ex aute. Laboris ipsum id est cillum minim quis sint ex culpa dolore minim. Lorem excepteur deserunt voluptate minim consequat qui quis enim. Do non irure pariatur exercitation commodo laboris sit. Sunt magna nulla magna Lorem reprehenderit dolore et tempor Lorem esse quis exercitation tempor commodo."
25+
],
26+
"qux": {
27+
"thud": "Et mollit occaecat et aliqua officia adipisicing adipisicing. Fugiat cillum dolor eu laborum cupidatat aliqua et reprehenderit do laboris velit in. Dolor voluptate Lorem pariatur voluptate enim labore in et pariatur consequat esse elit. Do qui aute proident in aliquip. Ea velit quis ex enim proident tempor laboris exercitation aute consectetur minim.",
28+
"fizz": {
29+
"buzz": "Sunt Lorem officia reprehenderit ea esse aliqua in veniam. Do irure amet dolore magna amet tempor anim sit irure tempor proident laborum dolore. Aute et ullamco eiusmod culpa et esse. Minim ut elit laboris est. Est mollit et mollit dolore ea adipisicing nostrud excepteur."
30+
}
31+
}
32+
});
1933

2034
Self {
2135
examples: vec![
@@ -40,6 +54,7 @@ impl Default for DemoApp {
4054
Box::new(CopyToClipboardExample::new(complex_object.clone())),
4155
Box::new(JsonEditorExample::new(complex_object.clone())),
4256
Box::new(ToggleButtonsCustomisationDemo::new(complex_object)),
57+
Box::new(WrappingExample::new(long_strings_object)),
4358
],
4459
open_example_idx: None,
4560
left_sidebar_expanded: true,
@@ -62,21 +77,23 @@ impl eframe::App for DemoApp {
6277

6378
ui.label(egui::RichText::new("Examples").monospace());
6479
ui.with_layout(egui::Layout::top_down_justified(egui::Align::LEFT), |ui| {
65-
egui::ScrollArea::vertical().show(ui, |ui| {
66-
for (idx, example) in self.examples.iter().enumerate() {
67-
let is_open = self
68-
.open_example_idx
69-
.is_some_and(|open_idx| open_idx == idx);
70-
71-
if ui.selectable_label(is_open, example.title()).clicked() {
72-
if is_open {
73-
self.open_example_idx = None;
74-
} else {
75-
self.open_example_idx = Some(idx);
80+
egui::ScrollArea::vertical()
81+
.auto_shrink([false, false])
82+
.show(ui, |ui| {
83+
for (idx, example) in self.examples.iter().enumerate() {
84+
let is_open = self
85+
.open_example_idx
86+
.is_some_and(|open_idx| open_idx == idx);
87+
88+
if ui.selectable_label(is_open, example.title()).clicked() {
89+
if is_open {
90+
self.open_example_idx = None;
91+
} else {
92+
self.open_example_idx = Some(idx);
93+
}
7694
}
7795
}
78-
}
79-
});
96+
});
8097
});
8198
});
8299

@@ -100,9 +117,11 @@ impl eframe::App for DemoApp {
100117
egui::CentralPanel::default().show(ctx, |ui| {
101118
match example {
102119
Some(example) => {
103-
egui::ScrollArea::vertical().show(ui, |ui| {
104-
example.show(ui);
105-
});
120+
egui::ScrollArea::vertical()
121+
.auto_shrink([false, false])
122+
.show(ui, |ui| {
123+
example.show(ui);
124+
});
106125
}
107126
None => {
108127
if !self.left_sidebar_expanded {

src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ pub mod value;
8989

9090
pub use default_expand::DefaultExpand;
9191
pub use response::JsonTreeResponse;
92-
pub use style::{JsonTreeStyle, JsonTreeVisuals};
92+
pub use style::{
93+
JsonTreeMaxWidth, JsonTreeStyle, JsonTreeVisuals, JsonTreeWrapping, JsonTreeWrappingConfig,
94+
};
9395
pub use toggle_buttons_state::ToggleButtonsState;
9496
pub use tree::JsonTree;

src/node.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
delimiters::{SpacingDelimiter, ARRAY_DELIMITERS, OBJECT_DELIMITERS},
1010
pointer::{JsonPointer, JsonPointerSegment},
1111
render::{
12-
JsonTreeRenderer, RenderBaseValueContext, RenderExpandableDelimiterContext,
12+
JsonTreeRenderer, ParentStatus, RenderBaseValueContext, RenderExpandableDelimiterContext,
1313
RenderPropertyContext, RenderSpacingDelimiterContext,
1414
},
1515
response::JsonTreeResponse,
@@ -94,7 +94,9 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
9494
) {
9595
match self.value.to_json_tree_value() {
9696
JsonTreeValue::Base(value, display_value, value_type) => {
97-
ui.horizontal_wrapped(|ui| {
97+
// Use horizontal instead of horizontal_wrapped so that the
98+
// base value always starts inline with the property and not below it.
99+
ui.horizontal(|ui| {
98100
ui.spacing_mut().item_spacing.x = 0.0;
99101

100102
if let Some(property) = self.parent {
@@ -127,6 +129,11 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
127129
pointer: JsonPointer(path_segments),
128130
style: &self.config.style,
129131
search_term: self.config.search_term.as_ref(),
132+
parent_status: if self.parent.is_some() {
133+
ParentStatus::ExpandedParent
134+
} else {
135+
ParentStatus::NoParent
136+
},
130137
},
131138
);
132139
});
@@ -259,6 +266,7 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
259266
pointer: JsonPointer(path_segments),
260267
style,
261268
search_term: search_term.as_ref(),
269+
parent_status: ParentStatus::CollapsedRoot,
262270
},
263271
);
264272
}

src/render.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ impl<'a, 'b, T: ToJsonTreeValue> DefaultRender for RenderPropertyContext<'a, 'b,
8787
}
8888
}
8989

90+
#[derive(Debug, Clone, Copy)]
91+
pub(crate) enum ParentStatus {
92+
NoParent,
93+
ExpandedParent,
94+
CollapsedRoot,
95+
}
96+
9097
/// A handle to the information of a render call for a non-recursive JSON value.
9198
pub struct RenderBaseValueContext<'a, 'b, T: ToJsonTreeValue> {
9299
/// The non-recursive JSON value being rendered.
@@ -100,6 +107,7 @@ pub struct RenderBaseValueContext<'a, 'b, T: ToJsonTreeValue> {
100107
/// The [`JsonTreeStyle`] that the [`JsonTree`](crate::JsonTree) was configured with.
101108
pub style: &'b JsonTreeStyle,
102109
pub(crate) search_term: Option<&'b SearchTerm>,
110+
pub(crate) parent_status: ParentStatus,
103111
}
104112

105113
impl<'a, 'b, T: ToJsonTreeValue> DefaultRender for RenderBaseValueContext<'a, 'b, T> {
@@ -110,6 +118,7 @@ impl<'a, 'b, T: ToJsonTreeValue> DefaultRender for RenderBaseValueContext<'a, 'b
110118
&self.display_value.to_string(),
111119
&self.value_type,
112120
self.search_term,
121+
self.parent_status,
113122
)
114123
}
115124
}
@@ -278,8 +287,9 @@ fn render_value(
278287
value_str: &str,
279288
value_type: &BaseValueType,
280289
search_term: Option<&SearchTerm>,
290+
parent_status: ParentStatus,
281291
) -> Response {
282-
let job = ui.ctx().memory_mut(|mem| {
292+
let mut job = ui.ctx().memory_mut(|mem| {
283293
mem.caches.cache::<ValueLayoutJobCreatorCache>().get((
284294
style.resolve_visuals(ui),
285295
value_str,
@@ -288,7 +298,7 @@ fn render_value(
288298
&style.resolve_font_id(ui),
289299
))
290300
});
291-
301+
job.wrap = style.resolve_value_text_wrapping(parent_status, ui);
292302
render_job(ui, job)
293303
}
294304

@@ -452,5 +462,6 @@ fn render_delimiter(ui: &mut Ui, style: &JsonTreeStyle, delimiter_str: &str) ->
452462
}
453463

454464
fn render_job(ui: &mut Ui, job: LayoutJob) -> Response {
455-
ui.add(Label::new(job).sense(Sense::click_and_drag()))
465+
let galley = ui.fonts(|f| f.layout_job(job));
466+
ui.add(Label::new(galley).sense(Sense::click_and_drag()))
456467
}

0 commit comments

Comments
 (0)