Skip to content

Commit 8a8af4d

Browse files
authored
Merge pull request #590 from kas-gui/push-ynyyunnrluqz
Fix several sizing bugs
2 parents 30c1448 + 255d4b1 commit 8a8af4d

File tree

10 files changed

+89
-30
lines changed

10 files changed

+89
-30
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,7 @@ members = [
167167
"examples/mandlebrot",
168168
"examples/text-editor",
169169
]
170+
171+
[patch.crates-io.kas-text]
172+
git = "https://github.com/kas-gui/kas-text.git"
173+
rev = "d2965eb7"

crates/kas-core/src/runner/window.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,23 @@ impl<A: AppData, G: GraphicsInstance, T: Theme<G::Shared>> Window<A, G, T> {
593593
debug_assert!(solve_cache.min(false) <= solve_cache.ideal(false));
594594
self.widget.resize_popups(&mut cx, data);
595595

596+
// Update window size restrictions: the new width may have changed height requirements
597+
let (restrict_min, restrict_max) = self.widget.properties().restrictions();
598+
window.set_min_inner_size(restrict_min.then(|| {
599+
window
600+
.solve_cache
601+
.min(true)
602+
.as_physical()
603+
.to_logical::<f64>(window.scale_factor())
604+
}));
605+
window.set_max_inner_size(restrict_max.then(|| {
606+
window
607+
.solve_cache
608+
.ideal(true)
609+
.as_physical()
610+
.to_logical::<f64>(window.scale_factor())
611+
}));
612+
596613
window.set_visible(true);
597614
window.request_redraw();
598615
log::trace!(

crates/kas-core/src/text/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
//! [KAS Text]: https://github.com/kas-gui/kas-text/
1515
1616
pub use kas_text::{
17-
Align, DPU, Direction, Effect, EffectFlags, MarkerPos, MarkerPosIter, NotReady, OwningVecIter,
18-
Range, Status, Text, TextDisplay, Vec2, fonts, format,
17+
Align, DPU, Direction, Effect, EffectFlags, Line, MarkerPos, MarkerPosIter, NotReady,
18+
OwningVecIter, Status, Text, TextDisplay, Vec2, fonts, format,
1919
};
2020

2121
mod selection;

crates/kas-core/src/theme/dimensions.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,10 @@ impl<D: 'static> ThemeSize for Window<D> {
353353
SizeRules::new(bound, bound, Stretch::Filler)
354354
}
355355
} else {
356-
let wrap_width = axis.other().map(|w| w.cast()).unwrap_or(f32::INFINITY);
356+
let wrap_width = wrap
357+
.then(|| axis.other().map(|w| w.cast()))
358+
.flatten()
359+
.unwrap_or(f32::INFINITY);
357360
let bound: i32 = text.measure_height(wrap_width).cast_ceil();
358361
SizeRules::new(bound, bound, Stretch::Filler)
359362
};

crates/kas-core/src/theme/size.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ pub trait ThemeSize {
235235
fn text_configure(&self, text: &mut dyn SizableText, class: TextClass);
236236

237237
/// Get [`SizeRules`] for a text element
238+
///
239+
/// Calculates required text dimensions according to the `class` and uses
240+
/// theme-defined margins.
238241
fn text_rules(&self, text: &mut dyn SizableText, class: TextClass, axis: AxisInfo)
239242
-> SizeRules;
240243
}

crates/kas-core/src/theme/text.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::text::fonts::FontSelector;
1515
use crate::text::format::FormattableText;
1616
use crate::text::*;
1717
use crate::{Action, Layout};
18+
use std::num::NonZeroUsize;
1819

1920
/// Text type-setting object (theme aware)
2021
///
@@ -442,13 +443,15 @@ impl<T: FormattableText> Text<T> {
442443
self.display.measure_width(max_width)
443444
}
444445

445-
/// Measure required vertical height
446+
/// Measure required vertical height, wrapping as configured
447+
///
448+
/// Stops after `max_lines`, if provided.
446449
///
447450
/// May partially prepare the text for display, but does not otherwise
448451
/// modify `self`.
449-
pub fn measure_height(&mut self, wrap_width: f32) -> f32 {
452+
pub fn measure_height(&mut self, wrap_width: f32, max_lines: Option<NonZeroUsize>) -> f32 {
450453
self.prepare_runs();
451-
self.display.measure_height(wrap_width)
454+
self.display.measure_height(wrap_width, max_lines)
452455
}
453456

454457
/// Prepare text for display, as necessary
@@ -552,6 +555,20 @@ impl<T: FormattableText> Text<T> {
552555
Ok(self.wrapped_display()?.num_lines())
553556
}
554557

558+
/// Get line properties
559+
#[inline]
560+
pub fn get_line(&self, index: usize) -> Result<Option<&Line>, NotReady> {
561+
Ok(self.wrapped_display()?.get_line(index))
562+
}
563+
564+
/// Iterate over line properties
565+
///
566+
/// [Requires status][Self#status-of-preparation]: lines have been wrapped.
567+
#[inline]
568+
pub fn lines(&self) -> Result<impl Iterator<Item = &Line>, NotReady> {
569+
Ok(self.wrapped_display()?.lines())
570+
}
571+
555572
/// Find the line containing text `index`
556573
///
557574
/// See [`TextDisplay::find_line`].
@@ -563,14 +580,6 @@ impl<T: FormattableText> Text<T> {
563580
Ok(self.wrapped_display()?.find_line(index))
564581
}
565582

566-
/// Get the range of a line, by line number
567-
///
568-
/// See [`TextDisplay::line_range`].
569-
#[inline]
570-
pub fn line_range(&self, line: usize) -> Result<Option<std::ops::Range<usize>>, NotReady> {
571-
Ok(self.wrapped_display()?.line_range(line))
572-
}
573-
574583
/// Get the directionality of the current line
575584
///
576585
/// See [`TextDisplay::line_is_rtl`].
@@ -705,6 +714,6 @@ impl<T: FormattableText> SizableText for Text<T> {
705714
}
706715

707716
fn measure_height(&mut self, wrap_width: f32) -> f32 {
708-
Text::measure_height(self, wrap_width)
717+
Text::measure_height(self, wrap_width, None)
709718
}
710719
}

crates/kas-widgets/src/edit/edit_field.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,15 @@ mod EditField {
102102
let dpem = cx.dpem();
103103
min = (self.width.0 * dpem).cast_ceil();
104104
ideal = (self.width.1 * dpem).cast_ceil();
105+
} else if let Some(width) = axis.other() {
106+
// Use the height of the first line as a reference
107+
let height = self
108+
.text
109+
.measure_height(width.cast(), std::num::NonZero::new(1));
110+
min = (self.lines.0 * height).cast_ceil();
111+
ideal = (self.lines.1 * height).cast_ceil();
105112
} else {
106-
// TODO: line height depends on the font; 1em is not a good
107-
// approximation. This code also misses inter-line spacing.
108-
let dpem = cx.dpem();
109-
min = (self.lines.0 * dpem).cast_ceil();
110-
ideal = (self.lines.1 * dpem).cast_ceil();
113+
unreachable!()
111114
};
112115

113116
let rules = self.text.size_rules(cx, axis);

crates/kas-widgets/src/scroll_label.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,13 @@ mod SelectableText {
4646
impl Layout for Self {
4747
fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
4848
let mut rules = kas::MacroDefinedLayout::size_rules(self, cx, axis);
49-
if axis.is_vertical() {
50-
rules.reduce_min_to(cx.dpem().cast_ceil());
49+
if axis.is_vertical()
50+
&& let Some(width) = axis.other()
51+
{
52+
let height = self
53+
.text
54+
.measure_height(width.cast(), std::num::NonZero::new(3));
55+
rules.reduce_min_to(height.cast_ceil());
5156
}
5257
rules
5358
}

crates/kas-widgets/src/stack.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,23 @@ mod Stack {
8888

8989
impl Layout for Self {
9090
fn size_rules(&mut self, cx: &mut SizeCx, axis: AxisInfo) -> SizeRules {
91-
let mut rules = SizeRules::EMPTY;
92-
for entry in self.widgets.iter_mut() {
91+
let (mut min, mut ideal) = (0, 0);
92+
let mut m = (0, 0);
93+
let mut stretch = Stretch::None;
94+
95+
for (i, entry) in self.widgets.iter_mut().enumerate() {
9396
if entry.1 != usize::MAX {
94-
rules = rules.max(entry.0.size_rules(cx, axis));
97+
let rules = entry.0.size_rules(cx, axis);
98+
ideal = ideal.max(rules.ideal_size());
99+
m = (m.0.max(rules.margins().0), m.1.max(rules.margins().1));
100+
stretch = stretch.max(rules.stretch());
101+
if i == self.active {
102+
min = rules.min_size();
103+
}
95104
}
96105
}
97-
rules
106+
107+
SizeRules::new(min, ideal, stretch).with_margins(m)
98108
}
99109

100110
fn set_rect(&mut self, cx: &mut SizeCx, rect: Rect, hints: AlignHints) {

examples/gallery.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,15 @@ KAS_CONFIG_MODE=readwrite
723723
)
724724
.unwrap();
725725

726-
let ui = column![ScrollLabel::new(desc), Separator::new(), EventConfig::new()]
727-
.map_any()
728-
.on_update(|cx, _, data: &AppData| cx.set_disabled(data.disabled));
729-
Page::new(ui)
726+
let ui = column![
727+
ScrollLabel::new(desc),
728+
Separator::new(),
729+
ScrollBarRegion::new(EventConfig::new())
730+
];
731+
Page::new(
732+
ui.map_any()
733+
.on_update(|cx, _, data: &AppData| cx.set_disabled(data.disabled)),
734+
)
730735
}
731736

732737
fn main() -> kas::runner::Result<()> {

0 commit comments

Comments
 (0)