A TextAndIcon button's label stays clear of its icon; tab labels stay inside their tab

SpriteIconButton.TextAndIcon.extractContents gives the label left = x + 2,
right = x + width - spriteWidth - 4 and centerX = x + width / 2, and
ActiveTextCollector.defaultScrollingHelper centres a label that fits on
Mth.clamp(centerX, left + lineWidth / 2, right - lineWidth / 2). Title's
Button::draw_label used x + 2 .. x + width - 2 for every button, so on the
first-launch Accessibility screen a label that fits (fil_ph "Settings sa
Aksesibilidad...", about 30 languages) ended under the icon and a wider one
(fr_ca, about 35 languages) slid under it.

ui::scrolling_text_around_at takes the centre; scrolling_text_at is the
default centre (left + right) / 2 and draws as before. Button::draw_label_at
passes the icon's room and the button's middle.

MenuTabBar.MenuTabButton.renderLabel hands the tab's label to
acceptScrollingWithDefaultCenter with a 1 px margin, its box 3 px lower on an
unselected tab, gray only on an inactive tab (WithInactiveMessage). The Create
World tabs drew it unclipped, so a label wider than its tab ran into the next
one; the Statistics tabs did too, drew every label at the selected tab's
height and grayed the active unselected ones. Both draw through
ui::menu_tab_label now.

The port ledger rows of acceptScrolling, RenderingTextCollector.acceptScrolling,
acceptScrollingWithDefaultCenter, defaultScrollingHelper,
extractScrollingStringOverContents, the slider's extractWidgetRenderState,
TextAndIcon.extractContents (it has a caller: the Accessibility screen) and
MenuTabButton.renderLabel say what is drawn now.

Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01RodDbPWCByh96WKcbiEQpB
This commit is contained in:
MariuslozoandClaude Sonnet 5.5 committed 2026-11-02 00:40:40 +01:00
1 parent 8d93d68f1d
commit dd428288d1
8 files changed
+297 -40

No files matched your search

+107 -8
View File
@@ -384,19 +384,42 @@ pub fn scrolling_text_at(
y: f32,
now_ms: i64,
color: u32,
) {
// `acceptScrollingWithDefaultCenter`: `centerX = (left + right) / 2`.
let center_x = left + ((right - left) / 2.0).floor();
scrolling_text_around_at(
batch, atlas, text, center_x, left, right, top, bottom, y, now_ms, color,
);
}
/// [`scrolling_text_at`] around an explicit `center_x`
/// (`ActiveTextCollector.acceptScrolling`, which `SpriteIconButton.TextAndIcon`
/// calls with the button's middle): a label that fits is centred on `center_x`
/// moved as far as it must to stay between `left` and `right`
/// (`Mth.clamp(centerX, left + lineWidth / 2, right - lineWidth / 2)`), so
/// beside an icon it ends where the icon's room begins. A wider one is drawn
/// from the left and slid inside the box.
#[allow(clippy::too_many_arguments)]
pub fn scrolling_text_around_at(
batch: &mut HudBatch,
atlas: &HudAtlas,
text: &Text,
center_x: f32,
left: f32,
right: f32,
top: f32,
bottom: f32,
y: f32,
now_ms: i64,
color: u32,
) {
let text = text.resolved();
let width = atlas.font.width_text(&text);
let room = right - left;
if width <= room {
batch.text_styled(
atlas,
&text,
centered_left(left, room, width),
y,
color,
true,
);
let half = (width / 2.0).floor();
let text_x = center_x.max(left + half).min(right - half);
batch.text_styled(atlas, &text, text_x - half, y, color, true);
return;
}
// Scissored to the widget: what does not fit is read as the label
@@ -440,6 +463,37 @@ pub fn scrolling_text(
);
}
/// `MenuTabBar.MenuTabButton.renderLabel` for the tab at `x, y`: its label
/// inside the tab less 1 px on each side, from the tab's top when selected
/// and 3 px lower when not (`acceptScrollingWithDefaultCenter`, so one wider
/// than the tab slides instead of running into the next tab). White, gray
/// for an inactive tab (`WithInactiveMessage`).
pub fn menu_tab_label(
batch: &mut HudBatch,
atlas: &HudAtlas,
label: &Text,
(x, y, width, height): (f32, f32, f32, f32),
selected: bool,
active: bool,
now_ms: i64,
) {
let top = y + if selected { 0.0 } else { 3.0 };
let bottom = y + height;
scrolling_text_at(
batch,
atlas,
label,
x + 1.0,
x + width - 1.0,
top,
bottom,
// defaultScrollingHelper: `(top + bottom - lineHeight) / 2 + 1`.
((top + bottom - 9.0) / 2.0).floor() + 1.0,
now_ms,
if active { u32::MAX } else { 0xFFA0_A0A0 },
);
}
/// Button (AbstractButton): nine-slice sprite plus the centred label.
pub fn button(
batch: &mut HudBatch,
@@ -1642,6 +1696,51 @@ mod tests {
);
}
/// `MenuTabButton.renderLabel` on a 24 px tab: centred on the tab, its
/// text top at `(0 + 24 - 9) / 2 + 1 = 8` when selected and at
/// `(3 + 24 - 9) / 2 + 1 = 10` when not; white on an active tab, selected
/// or not, `0xA0A0A0` on an inactive one (`WithInactiveMessage`). The
/// Statistics tabs drew every label at 8 and an active unselected one gray.
#[test]
fn a_tab_label_sits_lower_on_an_unselected_tab_and_is_gray_only_when_inactive() {
let atlas = atlas();
let label = Text::literal("Items");
let half = (atlas.font.width_text(&label.resolved()) / 2.0).floor();
// A 124 px tab at x = 100: `(left + right) / 2 = (101 + 223) / 2 = 162`.
let left = 162.0 - half;
for (selected, active, y, color) in [
(true, true, 8.0, u32::MAX),
(false, true, 10.0, u32::MAX),
(false, false, 10.0, 0xFFA0_A0A0),
] {
let mut tab = HudBatch::new([640, 480]);
menu_tab_label(
&mut tab,
&atlas,
&label,
(100.0, 0.0, 124.0, 24.0),
selected,
active,
0,
);
let mut want = HudBatch::new([640, 480]);
want.text_styled(&atlas, &label.resolved(), left, y, color, true);
let glyphs = |b: &HudBatch| -> Vec<([u32; 2], u32)> {
b.vertices
.iter()
.filter(|v| v.text == 1)
.map(|v| ([v.pos[0].to_bits(), v.pos[1].to_bits()], v.color))
.collect()
};
assert!(!glyphs(&want).is_empty());
assert_eq!(
glyphs(&tab),
glyphs(&want),
"selected {selected}, active {active}"
);
}
}
#[test]
fn slider_track_is_outlined_only_while_dragged() {
let (track, handle) = slider_sprites(true, true, false);
+7 -13
View File
@@ -326,20 +326,14 @@ impl Game<'_> {
(false, false) => "gui/sprites/widget/tab",
};
ui::nine_slice(batch, atlas, sprite, x, 0.0, tw, TAB_H, u32::MAX);
let label = Text::translate(key, &[]);
let lw = atlas.font.width_text(&label);
let color = if selected && active {
u32::MAX
} else {
0xFFA0_A0A0
};
batch.text_styled(
ui::menu_tab_label(
batch,
atlas,
&label,
(x + (tw - lw) / 2.0).floor(),
8.0,
color,
true,
&Text::translate(key, &[]),
(x, 0.0, tw, TAB_H),
selected,
active,
horizon_client::hud_overlays::now_ms(),
);
if selected {
batch.fill(atlas, x + 2.0, TAB_H - 3.0, tw - 4.0, 1.0, u32::MAX);
+25 -5
View File
@@ -470,7 +470,8 @@ pub(crate) struct Button {
enabled: bool,
id: u8,
icon: Option<(&'static str, f32)>,
/// `SpriteIconButton.TextAndIcon`: the icon at the right end, the text centred.
/// `SpriteIconButton.TextAndIcon`: the icon at the right end, the text
/// centred in the room left of it.
trailing: Option<(&'static str, f32)>,
}
impl Button {
@@ -479,17 +480,36 @@ impl Button {
/// (`AbstractWidget.extractScrollingStringOverContents`), instead of being
/// drawn past it over the buttons beside it (so_so's 590 px World Type
/// label on Create World, hi_in's Join Server in the Multiplayer footer).
/// A button with a `trailing` icon is `SpriteIconButton.TextAndIcon`, whose
/// label keeps clear of the icon: see [`Button::draw_label_at`].
fn draw_label(&self, batch: &mut HudBatch, atlas: &HudAtlas, color: u32) {
let label = Text::literal(&self.label);
crate::ui::scrolling_text(
self.draw_label_at(batch, atlas, color, crate::hud_overlays::now_ms());
}
/// [`Button::draw_label`] at `now_ms`. `SpriteIconButton.TextAndIcon`
/// (`extractContents`) hands the label to `acceptScrolling` with `left`
/// at the button's x plus 2, `right` at its x plus width minus the
/// sprite's width minus 4, and `centerX` at its x plus half its width: the
/// label slides inside that box when wider, and one that fits is moved
/// left until it ends where the icon's room begins. A button without an
/// icon has `right` 2 short of its edge, where `centerX` is the middle of
/// the box and nothing moves.
fn draw_label_at(&self, batch: &mut HudBatch, atlas: &HudAtlas, color: u32, now_ms: i64) {
let right = match self.trailing {
Some((_, size)) => self.x + self.w - size - 4.0,
None => self.x + self.w - 2.0,
};
crate::ui::scrolling_text_around_at(
batch,
atlas,
&label,
&Text::literal(&self.label),
self.x + (self.w / 2.0).floor(),
self.x + 2.0,
self.x + self.w - 2.0,
right,
self.y,
self.y + 20.0,
self.y + 6.0,
now_ms,
color,
);
}
@@ -535,15 +535,14 @@ impl Title {
u32::MAX,
);
}
// The label sits 3 pixels lower on unselected tabs.
let top = if selected { 0.0 } else { 3.0 };
batch.text_styled(
ui::menu_tab_label(
batch,
atlas,
&label,
x + (tw / 2.0).floor() - (lw / 2.0).floor(),
((top + TAB_BAR - 9.0) / 2.0).floor() + 1.0,
u32::MAX,
(x, 0.0, tw, TAB_BAR),
selected,
true,
crate::hud_overlays::now_ms(),
);
}
let (gx, gy) = grid(w, h, o.tab);
+145
View File
@@ -2027,3 +2027,148 @@ fn labels_at(f: &Fixture) -> (f32, f32, (f32, f32)) {
(customize.x + 10.0, customize.y + 10.0),
)
}
/// A button with an icon at its right end (`SpriteIconButton.TextAndIcon`, the
/// Music & Sounds button of the Accessibility screen): `extractContents` gives
/// the label `left = x + 2` and `right = x + width - spriteWidth - 4`.
mod text_and_icon {
use super::*;
fn atlas() -> HudAtlas {
HudAtlas::build(std::path::Path::new("/nonexistent/horizon-test-assets")).unwrap()
}
fn button(label: &str, icon: Option<(&'static str, f32)>) -> Button {
Button {
x: 30.0,
y: 40.0,
w: 150.0,
label: label.into(),
enabled: true,
id: 0,
icon: None,
trailing: icon,
}
}
/// The x of every vertex of the label's text.
fn xs(batch: &HudBatch) -> Vec<f32> {
batch
.vertices
.iter()
.filter(|v| v.text == 1)
.map(|v| v.pos[0])
.collect()
}
/// The first `n` letters of a text with no repeating pattern.
fn label(n: usize) -> String {
"Bezpostrzydnie połōnczynie Direct Connection Join Server"
.chars()
.take(n)
.collect()
}
fn width(atlas: &HudAtlas, label: &str) -> i32 {
HudBatch::text_width(atlas, label).ceil() as i32
}
/// A label that fits beside the icon but, centred on the button, would end
/// under it (`Settings sa Aksesibilidad...` in `fil_ph`, and about 40 other
/// languages) is moved left until it ends where the icon's room begins:
/// `Mth.clamp(centerX, left + lineWidth / 2, right - lineWidth / 2)` in
/// `defaultScrollingHelper`. It does not scroll. The same label in a plain
/// button stays centred.
#[test]
fn a_label_that_fits_beside_the_icon_is_moved_clear_of_it() {
let atlas = atlas();
// A 16 px icon: `left = x + 2`, `right = x + 130`, `centerX = x + 75`.
let (left, right, center) = (32, 30 + 130, 30 + 75);
// The widest label that fits; `right - width / 2 < centerX` by most.
let text = (1..60)
.map(label)
.rfind(|t| width(&atlas, t) <= right - left)
.expect("a label that fits");
let half = width(&atlas, &text) / 2;
assert!(right - half < center, "{text}: {}", width(&atlas, &text));
let drawn_from = |from: i32| {
let mut batch = HudBatch::new([640, 480]);
batch.text(&atlas, &text, from as f32, 46.0, u32::MAX, true);
xs(&batch)
};
let draw = |b: &Button, now_ms: i64| {
let mut batch = HudBatch::new([640, 480]);
b.draw_label_at(&mut batch, &atlas, u32::MAX, now_ms);
xs(&batch)
};
let b = button(&text, Some(("icon/music_notes", 16.0)));
// Vanilla: `textX = right - width / 2`, drawn from `textX - width / 2`.
assert!(draw(&b, 0) == drawn_from(right - half - half), "{text}");
for now_ms in [700, 1500, 31_337] {
assert!(draw(&b, now_ms) == draw(&b, 0), "it scrolls at {now_ms}");
}
let plain = button(&text, None);
assert!(draw(&plain, 0) == drawn_from(center - half), "plain {text}");
}
/// A label wider than the room beside the icon scrolls inside
/// `x + 2 ..= x + width - spriteWidth - 4`, never under the icon.
#[test]
fn a_label_wider_than_the_room_beside_the_icon_scrolls_clear_of_it() {
let atlas = atlas();
let text = label(40);
let b = button(&text, Some(("icon/music_notes", 16.0)));
assert!(width(&atlas, &text) > 130 - 2);
let mut seen = std::collections::BTreeSet::new();
for now_ms in (0..40_000).step_by(250) {
let mut batch = HudBatch::new([640, 480]);
b.draw_label_at(&mut batch, &atlas, u32::MAX, now_ms);
let xs = xs(&batch);
assert!(!xs.is_empty());
for x in &xs {
assert!(
(32.0..=30.0 + 150.0 - 16.0 - 4.0).contains(x),
"{x} at {now_ms} ms"
);
}
seen.insert(xs.iter().map(|x| x.to_bits()).collect::<Vec<_>>());
}
assert!(seen.len() > 3, "the label hardly moves");
}
}
/// `MenuTabBar.MenuTabButton.renderLabel`: a tab's label slides inside the
/// tab's box with a 1 px margin when it is wider than the tab (here a window
/// too narrow for the English labels), instead of running into the next tab.
#[test]
fn a_create_world_tab_label_wider_than_its_tab_stays_inside_it() {
let mut f = Fixture::new();
f.click("Singleplayer");
let atlas = HudAtlas::build(std::path::Path::new("/nonexistent/horizon-test-assets")).unwrap();
// 90 px wide: three 20 px tabs from x = 14, too narrow for "Game".
let mut batch = HudBatch::new([90, 262]);
f.title.draw(&atlas, &mut batch, (0.0, 100.0), true);
let labels: Vec<f32> = batch
.vertices
.iter()
.filter(|v| v.text == 1 && v.pos[1] < 24.0)
.map(|v| v.pos[0])
.collect();
assert!(!labels.is_empty());
let boxes: Vec<(f32, f32)> = (0..3)
.map(|tab| {
(
14.0 + 20.0 * tab as f32 + 1.0,
14.0 + 20.0 * (tab + 1) as f32 - 1.0,
)
})
.collect();
for x in &labels {
assert!(
boxes.iter().any(|(lo, hi)| (*lo..=*hi).contains(x)),
"label vertex at {x} is outside every tab's box"
);
}
for (tab, (lo, hi)) in boxes.iter().enumerate() {
assert!(
labels.iter().any(|x| (*lo..=*hi).contains(x)),
"tab {tab} draws no label"
);
}
}
+6 -6
View File
@@ -1,7 +1,7 @@
ActiveTextCollector.accept inline crates/horizon-client-render/src/screen_draw.rs::text default overload: left-aligned text at x,y with default parameters
ActiveTextCollector.acceptScrolling port crates/horizon-client-render/src/screen_draw.rs::scrolling_text gap: minor: only the screen_draw widgets (Button) scroll; ui.rs centered_text clips
ActiveTextCollector.acceptScrollingWithDefaultCenter port crates/horizon-client-render/src/ui.rs::centered_text gap: minor: centres the message; wider text is clipped instead of scrolled
ActiveTextCollector.defaultScrollingHelper port crates/horizon-client-render/src/screen_draw.rs::scrolling_text gap: minor: only the screen_draw widgets (Button) call it; ui.rs centered_text still clips wider text. The offset is screen_draw.rs::scroll_offset
ActiveTextCollector.acceptScrolling port crates/horizon-client-render/src/ui.rs::scrolling_text_around_at the HudBatch widgets; a label that fits is clamped between left and right around centerX (title.rs::Button::draw_label_at passes the middle of a TextAndIcon button); screen_draw.rs::scrolling_text is the same for the screen_draw widgets
ActiveTextCollector.acceptScrollingWithDefaultCenter port crates/horizon-client-render/src/ui.rs::scrolling_text_at centre (left + right) / 2; ui::button, ui::slider and the tab labels (ui::menu_tab_label) draw through it
ActiveTextCollector.defaultScrollingHelper port crates/horizon-client-render/src/screen_draw.rs::scrolling_text the offset is ui.rs::scroll_offset (screen_draw.rs::scroll_offset for the screen_draw widgets); ui.rs::centered_text, for titles and text lines, still clips because vanilla does not scroll them
ActiveTextCollector.findElementUnderCursor port crates/horizon-client-render/src/chat.rs::Chat::style_at gap: minor: hit test covers chat lines only; book links use game/books.rs book_link_at
ActiveTextCollector.isPointInRectangle port crates/horizon-client-render/src/ui.rs::hovered half-open rectangle test with the same bounds
ActiveTextCollector.ClickableStyleFinder.accept port crates/horizon-client-render/src/chat.rs::Chat::style_at gap: minor: only chat lines are searched for click and hover styles
@@ -62,7 +62,7 @@ GuiGraphicsExtractor.textRendererForWidget skip:ui builds a text collector for
GuiGraphicsExtractor.textRenderer skip:ui builds a text collector; Horizon draws text directly
GuiGraphicsExtractor.createDefaultTextParameters skip:ui default pose, opacity and scissor for text; Horizon passes colour and rectangle directly
GuiGraphicsExtractor.RenderingTextCollector.accept port crates/horizon-client-render/src/screen_draw.rs::text gap: minor: no hover or click registration here (see Chat::style_at)
GuiGraphicsExtractor.RenderingTextCollector.acceptScrolling port crates/horizon-client-render/src/screen_draw.rs::scrolling_text gap: minor: only the screen_draw widgets (Button) scroll; ui.rs centered_text clips
GuiGraphicsExtractor.RenderingTextCollector.acceptScrolling port crates/horizon-client-render/src/screen_draw.rs::scrolling_text the font's width and a 9 px line into defaultScrollingHelper, for the screen_draw widgets; ui.rs::scrolling_text_around_at is the same for the HudBatch widgets
GuiGraphicsExtractor.ScissorStack.push inline crates/horizon-client-render/src/screen_draw.rs::paint gap: minor: the pushed rectangle replaces the current one; it is not intersected
GuiGraphicsExtractor.ScissorStack.pop inline crates/horizon-client-render/src/screen_draw.rs::paint gap: minor: Draw::NoScissor clears the scissor instead of restoring the enclosing one
GuiGraphicsExtractor.ScissorStack.containsPoint missing minor: hover ignores the scissor, so a widget clipped out of view can still highlight
@@ -121,7 +121,7 @@ AbstractSelectionList.TrackedList.set skip:java delegate list wrapper
AbstractSliderButton.getSprite inline crates/horizon-client-render/src/ui.rs::slider gap: minor: the track outline is drawn while dragged only; keyboard focus does not highlight it; hover highlights only the handle
AbstractSliderButton.getHandleSprite inline crates/horizon-client-render/src/ui.rs::slider gap: minor: handle highlight follows hover only
AbstractSliderButton.updateWidgetNarration skip:other narration only
AbstractSliderButton.extractWidgetRenderState port crates/horizon-client-render/src/ui.rs::slider gap: minor: the label is clipped rather than scrolled
AbstractSliderButton.extractWidgetRenderState port crates/horizon-client-render/src/ui.rs::slider_ext the whole message, value included, scrolls inside the slider (margin 2)
AbstractSliderButton.handleCursor port crates/horizon-client-render/src/widgets.rs::widget_cursor gap: minor: no resize cursor while dragging
AbstractSliderButton.setFocused missing minor: keyboard focus does not enable value changes; there is no change-value state
AbstractSliderButton.keyPressed missing minor: Left and Right do not move a focused slider; Enter does not toggle it
@@ -139,7 +139,7 @@ AbstractTextAreaWidget.extractBorder skip:ui frame sprite; no behaviour
AbstractTextAreaWidget.withinContentAreaTopBottom skip:ui visibility cull while drawing lines
AbstractWidget.extractRenderState port crates/horizon-client-render/src/ui.rs::hovered gap: minor: the scissor test is not part of the hover check
AbstractWidget.extractTooltipForNextRenderPass port crates/horizon-client-render/src/ui.rs::tooltip gap: minor: unsure: widget hover delay not checked
AbstractWidget.extractScrollingStringOverContents port crates/horizon-client-render/src/ui.rs::centered_text gap: minor: text is clipped, not scrolled
AbstractWidget.extractScrollingStringOverContents port crates/horizon-client-render/src/ui.rs::scrolling_text ui::button and ui::slider (margin 2) draw it for the HudBatch widgets; screen_draw.rs::scrolling_string_over_contents for the screen_draw ones
AbstractWidget.mouseClicked port crates/horizon-client/src/title.rs::Title::click gap: minor: click dispatch is per screen; the press sound is audio.rs Audio::play_button_click
AbstractWidget.mouseReleased port crates/horizon-client-render/src/ui.rs::Scroll::release gap: minor: only the scroll release; widget onRelease is per screen
AbstractWidget.mouseDragged port crates/horizon-client-render/src/ui.rs::Scroll::drag gap: minor: only scroll drag; widget onDrag is per screen
1 ActiveTextCollector.accept inline crates/horizon-client-render/src/screen_draw.rs::text default overload: left-aligned text at x,y with default parameters
2 ActiveTextCollector.acceptScrolling port crates/horizon-client-render/src/screen_draw.rs::scrolling_text crates/horizon-client-render/src/ui.rs::scrolling_text_around_at gap: minor: only the screen_draw widgets (Button) scroll; ui.rs centered_text clips the HudBatch widgets; a label that fits is clamped between left and right around centerX (title.rs::Button::draw_label_at passes the middle of a TextAndIcon button); screen_draw.rs::scrolling_text is the same for the screen_draw widgets
3 ActiveTextCollector.acceptScrollingWithDefaultCenter port crates/horizon-client-render/src/ui.rs::centered_text crates/horizon-client-render/src/ui.rs::scrolling_text_at gap: minor: centres the message; wider text is clipped instead of scrolled centre (left + right) / 2; ui::button, ui::slider and the tab labels (ui::menu_tab_label) draw through it
4 ActiveTextCollector.defaultScrollingHelper port crates/horizon-client-render/src/screen_draw.rs::scrolling_text gap: minor: only the screen_draw widgets (Button) call it; ui.rs centered_text still clips wider text. The offset is screen_draw.rs::scroll_offset the offset is ui.rs::scroll_offset (screen_draw.rs::scroll_offset for the screen_draw widgets); ui.rs::centered_text, for titles and text lines, still clips because vanilla does not scroll them
5 ActiveTextCollector.findElementUnderCursor port crates/horizon-client-render/src/chat.rs::Chat::style_at gap: minor: hit test covers chat lines only; book links use game/books.rs book_link_at
6 ActiveTextCollector.isPointInRectangle port crates/horizon-client-render/src/ui.rs::hovered half-open rectangle test with the same bounds
7 ActiveTextCollector.ClickableStyleFinder.accept port crates/horizon-client-render/src/chat.rs::Chat::style_at gap: minor: only chat lines are searched for click and hover styles
62 GuiGraphicsExtractor.textRenderer skip:ui builds a text collector; Horizon draws text directly
63 GuiGraphicsExtractor.createDefaultTextParameters skip:ui default pose, opacity and scissor for text; Horizon passes colour and rectangle directly
64 GuiGraphicsExtractor.RenderingTextCollector.accept port crates/horizon-client-render/src/screen_draw.rs::text gap: minor: no hover or click registration here (see Chat::style_at)
65 GuiGraphicsExtractor.RenderingTextCollector.acceptScrolling port crates/horizon-client-render/src/screen_draw.rs::scrolling_text gap: minor: only the screen_draw widgets (Button) scroll; ui.rs centered_text clips the font's width and a 9 px line into defaultScrollingHelper, for the screen_draw widgets; ui.rs::scrolling_text_around_at is the same for the HudBatch widgets
66 GuiGraphicsExtractor.ScissorStack.push inline crates/horizon-client-render/src/screen_draw.rs::paint gap: minor: the pushed rectangle replaces the current one; it is not intersected
67 GuiGraphicsExtractor.ScissorStack.pop inline crates/horizon-client-render/src/screen_draw.rs::paint gap: minor: Draw::NoScissor clears the scissor instead of restoring the enclosing one
68 GuiGraphicsExtractor.ScissorStack.containsPoint missing minor: hover ignores the scissor, so a widget clipped out of view can still highlight
121 AbstractSliderButton.getSprite inline crates/horizon-client-render/src/ui.rs::slider gap: minor: the track outline is drawn while dragged only; keyboard focus does not highlight it; hover highlights only the handle
122 AbstractSliderButton.getHandleSprite inline crates/horizon-client-render/src/ui.rs::slider gap: minor: handle highlight follows hover only
123 AbstractSliderButton.updateWidgetNarration skip:other narration only
124 AbstractSliderButton.extractWidgetRenderState port crates/horizon-client-render/src/ui.rs::slider crates/horizon-client-render/src/ui.rs::slider_ext gap: minor: the label is clipped rather than scrolled the whole message, value included, scrolls inside the slider (margin 2)
125 AbstractSliderButton.handleCursor port crates/horizon-client-render/src/widgets.rs::widget_cursor gap: minor: no resize cursor while dragging
126 AbstractSliderButton.setFocused missing minor: keyboard focus does not enable value changes; there is no change-value state
127 AbstractSliderButton.keyPressed missing minor: Left and Right do not move a focused slider; Enter does not toggle it
139 AbstractTextAreaWidget.withinContentAreaTopBottom skip:ui visibility cull while drawing lines
140 AbstractWidget.extractRenderState port crates/horizon-client-render/src/ui.rs::hovered gap: minor: the scissor test is not part of the hover check
141 AbstractWidget.extractTooltipForNextRenderPass port crates/horizon-client-render/src/ui.rs::tooltip gap: minor: unsure: widget hover delay not checked
142 AbstractWidget.extractScrollingStringOverContents port crates/horizon-client-render/src/ui.rs::centered_text crates/horizon-client-render/src/ui.rs::scrolling_text gap: minor: text is clipped, not scrolled ui::button and ui::slider (margin 2) draw it for the HudBatch widgets; screen_draw.rs::scrolling_string_over_contents for the screen_draw ones
143 AbstractWidget.mouseClicked port crates/horizon-client/src/title.rs::Title::click gap: minor: click dispatch is per screen; the press sound is audio.rs Audio::play_button_click
144 AbstractWidget.mouseReleased port crates/horizon-client-render/src/ui.rs::Scroll::release gap: minor: only the scroll release; widget onRelease is per screen
145 AbstractWidget.mouseDragged port crates/horizon-client-render/src/ui.rs::Scroll::drag gap: minor: only scroll drag; widget onDrag is per screen
+1 -1
View File
@@ -72,7 +72,7 @@ SpriteIconButton.extractLoadingStateIfLoading skip:service loading spinner only
SpriteIconButton.Builder.sprite skip:java builder setter; Horizon draws its icon buttons with fixed sprites
SpriteIconButton.Builder.build skip:java builder; no object is needed
SpriteIconButton.CenteredIcon.extractContents skip:service FriendsButton (social)
SpriteIconButton.TextAndIcon.extractContents skip:other no caller in 26.3
SpriteIconButton.TextAndIcon.extractContents port crates/horizon-client/src/title.rs::Button::draw_label_at the Accessibility screen's Music & Sounds, Accessibility Settings and Language buttons (AccessibilityOnboardingScreen): the label in x + 2 .. x + width - icon - 4 centred on the button's middle, the icon drawn in Title::draw
StringWidget.setMaxWidth port crates/horizon-client-render/src/screen_draw.rs::string_widget clips at max_width (CLAMPED)
StringWidget.getWidth port crates/horizon-client-render/src/screen_draw.rs::label_width the smaller of max width and text width
StringWidget.visitLines port crates/horizon-client-render/src/screen_draw.rs::string_widget gap: minor: SCROLLING overflow (scrolling text) is not ported; CLAMPED draws "..."
Can't render this file because it contains an unexpected character in line 35 and column 134.
+1 -1
View File
@@ -20,7 +20,7 @@ MenuTabBar.Builder.addTabs skip:java builder
MenuTabBar.Builder.build skip:java builder
MenuTabBar.MenuTabButton.extractWidgetRenderState inline crates/horizon-client/src/title/create_world.rs::Title::draw_create tab loop: sprite by selected and hover state, underline, label
MenuTabBar.MenuTabButton.renderMenuBackground inline crates/horizon-client/src/title/create_world.rs::Title::draw_create menu background inside the selected tab
MenuTabBar.MenuTabButton.renderLabel inline crates/horizon-client/src/title/create_world.rs::Title::draw_create label centred, 3 px lower on unselected tabs
MenuTabBar.MenuTabButton.renderLabel port crates/horizon-client-render/src/ui.rs::menu_tab_label the Create World and Statistics tabs: centred, 3 px lower on unselected tabs, gray on inactive ones; a label wider than the tab (margin 1) scrolls
MenuTabBar.MenuTabButton.renderFocusUnderline inline crates/horizon-client/src/title/create_world.rs::Title::draw_create 1 px underline under the selected tab label
TabManager.setTabArea skip:ui tab area layout; create world computes the tab row on every draw (tabs())
TabManager.setCurrentTab port crates/horizon-client/src/title.rs::Title::click a click on a tab sets options.tab
1 ContainerEventHandler.getChildAt port crates/horizon-client-render/src/pack_screen.rs::PackScreen::button_at gap: minor: says whether a button is under the pointer; list rows are hit-tested in PackScreen::click
20 MenuTabBar.Builder.build skip:java builder
21 MenuTabBar.MenuTabButton.extractWidgetRenderState inline crates/horizon-client/src/title/create_world.rs::Title::draw_create tab loop: sprite by selected and hover state, underline, label
22 MenuTabBar.MenuTabButton.renderMenuBackground inline crates/horizon-client/src/title/create_world.rs::Title::draw_create menu background inside the selected tab
23 MenuTabBar.MenuTabButton.renderLabel inline port crates/horizon-client/src/title/create_world.rs::Title::draw_create crates/horizon-client-render/src/ui.rs::menu_tab_label label centred, 3 px lower on unselected tabs the Create World and Statistics tabs: centred, 3 px lower on unselected tabs, gray on inactive ones; a label wider than the tab (margin 1) scrolls
24 MenuTabBar.MenuTabButton.renderFocusUnderline inline crates/horizon-client/src/title/create_world.rs::Title::draw_create 1 px underline under the selected tab label
25 TabManager.setTabArea skip:ui tab area layout; create world computes the tab row on every draw (tabs())
26 TabManager.setCurrentTab port crates/horizon-client/src/title.rs::Title::click a click on a tab sets options.tab