Skip to content

Commit

Permalink
refactor label truncation and slider rendering to work together,
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
douira committed Aug 7, 2024
1 parent 63798c0 commit 8e7ea7f
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,50 @@ public ControlElement(Option<T> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ private static class Button extends ControlElement<Integer> {
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;
Expand Down Expand Up @@ -75,16 +76,6 @@ public Button(Option<Integer> 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();
Expand All @@ -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() {
Expand Down

0 comments on commit 8e7ea7f

Please sign in to comment.