From 8e7ea7fcc0778d63761fd4fc303d0673f7d57702 Mon Sep 17 00:00:00 2001 From: douira Date: Wed, 7 Aug 2024 03:53:19 +0200 Subject: [PATCH] refactor label truncation and slider rendering to work together, this means the label will now be truncated based on the slider's current actual content width. other control elements can but don't have to implement accurate content width information. --- .../gui/options/control/ControlElement.java | 40 +++++++++++++-- .../gui/options/control/SliderControl.java | 51 +++++++++---------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java b/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java index 3df9998470..b5492c75b1 100644 --- a/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java +++ b/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/ControlElement.java @@ -20,18 +20,50 @@ public ControlElement(Option option, Dim2i dim) { this.dim = dim; } + public int getContentWidth() { + return this.option.getControl().getMaxWidth(); + } + @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { String name = this.option.getName().getString(); - String label; - if ((this.hovered || this.isFocused()) && this.font.width(name) > (this.dim.width() - this.option.getControl().getMaxWidth())) { - name = name.substring(0, Math.min(name.length(), 10)) + "..."; + // add the star suffix before truncation to prevent it from overlapping with the label text + if (this.option.isAvailable() && this.option.hasChanged()) { + name = name + " *"; } + // on focus or hover truncate the label to never overlap with the control's content + if (this.hovered || this.isFocused()) { + var suffix = "..."; + var suffixWidth = this.font.width(suffix); + var nameFontWidth = this.font.width(name); + var targetWidth = this.dim.width() - this.getContentWidth() - 20; + if (nameFontWidth > targetWidth) { + targetWidth -= suffixWidth; + int maxLabelChars = name.length() - 3; + int minLabelChars = 1; + + // binary search on how many chars fit + while (maxLabelChars - minLabelChars > 1) { + var mid = (maxLabelChars + minLabelChars) / 2; + var midName = name.substring(0, mid); + var midWidth = this.font.width(midName); + if (midWidth > targetWidth) { + maxLabelChars = mid; + } else { + minLabelChars = mid; + } + } + + name = name.substring(0, minLabelChars).trim() + suffix; + } + } + + String label; if (this.option.isAvailable()) { if (this.option.hasChanged()) { - label = ChatFormatting.ITALIC + name + " *"; + label = ChatFormatting.ITALIC + name; } else { label = ChatFormatting.WHITE + name; } diff --git a/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java b/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java index 402191ee52..11097c6cc6 100644 --- a/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java +++ b/src/main/java/net/caffeinemc/mods/sodium/client/gui/options/control/SliderControl.java @@ -48,6 +48,7 @@ private static class Button extends ControlElement { private static final int THUMB_WIDTH = 2, TRACK_HEIGHT = 1; private final Rect2i sliderBounds; + private int contentWidth; private final ControlValueFormatter formatter; private final int min; @@ -75,16 +76,6 @@ public Button(Option option, Dim2i dim, int min, int max, int interval, @Override public void render(GuiGraphics graphics, int mouseX, int mouseY, float delta) { - super.render(graphics, mouseX, mouseY, delta); - - if (this.option.isAvailable() && (this.hovered || this.isFocused())) { - this.renderSlider(graphics); - } else { - this.renderStandaloneValue(graphics); - } - } - - private void renderStandaloneValue(GuiGraphics graphics) { int sliderX = this.sliderBounds.getX(); int sliderY = this.sliderBounds.getY(); int sliderWidth = this.sliderBounds.getWidth(); @@ -93,30 +84,36 @@ private void renderStandaloneValue(GuiGraphics graphics) { Component label = this.formatter.format(this.option.getValue()); int labelWidth = this.font.width(label); - this.drawString(graphics, label, sliderX + sliderWidth - labelWidth, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); - } - - private void renderSlider(GuiGraphics graphics) { - int sliderX = this.sliderBounds.getX(); - int sliderY = this.sliderBounds.getY(); - int sliderWidth = this.sliderBounds.getWidth(); - int sliderHeight = this.sliderBounds.getHeight(); + boolean drawSlider = this.option.isAvailable() && (this.hovered || this.isFocused()); + if (drawSlider) { + this.contentWidth = sliderWidth + labelWidth; + } else { + this.contentWidth = labelWidth; + } - this.thumbPosition = this.getThumbPositionForValue(this.option.getValue()); + // render the label first and then the slider to prevent the highlight rect from darkening the slider + super.render(graphics, mouseX, mouseY, delta); - double thumbOffset = Mth.clamp((double) (this.getIntValue() - this.min) / this.range * sliderWidth, 0, sliderWidth); + if (drawSlider) { + this.thumbPosition = this.getThumbPositionForValue(this.option.getValue()); - int thumbX = (int) (sliderX + thumbOffset - THUMB_WIDTH); - int trackY = (int) (sliderY + (sliderHeight / 2f) - ((double) TRACK_HEIGHT / 2)); + double thumbOffset = Mth.clamp((double) (this.getIntValue() - this.min) / this.range * sliderWidth, 0, sliderWidth); - this.drawRect(graphics, thumbX, sliderY, thumbX + (THUMB_WIDTH * 2), sliderY + sliderHeight, 0xFFFFFFFF); - this.drawRect(graphics, sliderX, trackY, sliderX + sliderWidth, trackY + TRACK_HEIGHT, 0xFFFFFFFF); + int thumbX = (int) (sliderX + thumbOffset - THUMB_WIDTH); + int trackY = (int) (sliderY + (sliderHeight / 2f) - ((double) TRACK_HEIGHT / 2)); - Component label = this.formatter.format(this.getIntValue()); + this.drawRect(graphics, thumbX, sliderY, thumbX + (THUMB_WIDTH * 2), sliderY + sliderHeight, 0xFFFFFFFF); + this.drawRect(graphics, sliderX, trackY, sliderX + sliderWidth, trackY + TRACK_HEIGHT, 0xFFFFFFFF); - int labelWidth = this.font.width(label); + this.drawString(graphics, label, sliderX - labelWidth - 6, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + } else { + this.drawString(graphics, label, sliderX + sliderWidth - labelWidth, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + } + } - this.drawString(graphics, label, sliderX - labelWidth - 6, sliderY + (sliderHeight / 2) - 4, 0xFFFFFFFF); + @Override + public int getContentWidth() { + return this.contentWidth; } public int getIntValue() {