Scroller with correct clipping
This commit is contained in:
parent
472aa16967
commit
67d21f3fba
10 changed files with 142 additions and 27 deletions
|
@ -2,8 +2,8 @@ package org.betterx.bclib.client.gui.modmenu;
|
||||||
|
|
||||||
import org.betterx.ui.layout.components.*;
|
import org.betterx.ui.layout.components.*;
|
||||||
import org.betterx.ui.layout.values.DynamicSize;
|
import org.betterx.ui.layout.values.DynamicSize;
|
||||||
|
import org.betterx.ui.vanilla.LayoutScreen;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
|
||||||
import net.minecraft.client.gui.screens.Screen;
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
@ -11,23 +11,28 @@ import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.Environment;
|
import net.fabricmc.api.Environment;
|
||||||
|
|
||||||
@Environment(EnvType.CLIENT)
|
@Environment(EnvType.CLIENT)
|
||||||
public class TestScreen extends Screen {
|
public class TestScreen extends LayoutScreen {
|
||||||
public TestScreen(Component component) {
|
public TestScreen(Component component) {
|
||||||
super(component);
|
super(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
Panel main;
|
public TestScreen(Screen parent, Component component) {
|
||||||
|
super(parent, component);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected org.betterx.ui.layout.components.Component<?> initContent() {
|
||||||
super.init();
|
VerticalStack rows = new VerticalStack(DynamicSize.fit(), DynamicSize.fitOrFill());
|
||||||
main = new Panel(this.width, this.height);
|
|
||||||
VerticalStack rows = new VerticalStack(DynamicSize.fit(), DynamicSize.relative(1));
|
|
||||||
|
|
||||||
rows.addFiller();
|
rows.addFiller();
|
||||||
rows.add(new Text(
|
rows.add(new Text(
|
||||||
DynamicSize.fitOrFill(), DynamicSize.fit(),
|
DynamicSize.fitOrFill(), DynamicSize.fit(),
|
||||||
Component.literal("Some Text")
|
Component.literal("Some Text")
|
||||||
|
).alignRight()
|
||||||
|
);
|
||||||
|
rows.add(new Text(
|
||||||
|
DynamicSize.fitOrFill(), DynamicSize.fit(),
|
||||||
|
Component.literal("Some other, longer Text")
|
||||||
).centerHorizontal()
|
).centerHorizontal()
|
||||||
);
|
);
|
||||||
rows.addSpacer(16);
|
rows.addSpacer(16);
|
||||||
|
@ -67,7 +72,7 @@ public class TestScreen extends Screen {
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
rows.add(cb1);
|
rows.add(cb1);
|
||||||
rows.addSpacer(16);
|
rows.addSpacer(32);
|
||||||
rows.add(new Button(
|
rows.add(new Button(
|
||||||
DynamicSize.fit(), DynamicSize.fit(),
|
DynamicSize.fit(), DynamicSize.fit(),
|
||||||
Component.literal("test"),
|
Component.literal("test"),
|
||||||
|
@ -90,15 +95,7 @@ public class TestScreen extends Screen {
|
||||||
).centerHorizontal()
|
).centerHorizontal()
|
||||||
);
|
);
|
||||||
rows.addFiller();
|
rows.addFiller();
|
||||||
main.setChild(HorizontalStack.centered(rows));
|
|
||||||
main.calculateLayout();
|
|
||||||
|
|
||||||
addRenderableWidget(main);
|
return HorizontalStack.centered(VerticalScroll.create(DynamicSize.fit(), DynamicSize.relative(1), rows));
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(PoseStack poseStack, int i, int j, float f) {
|
|
||||||
renderDirtBackground(i);
|
|
||||||
super.render(poseStack, i, j, f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ public class ModMenuEntryPoint implements ModMenuApi {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
public ConfigScreenFactory<?> getModConfigScreenFactory() {
|
||||||
return (parent) -> new TestScreen(Component.literal("Hello Test"));
|
return (parent) -> new TestScreen(parent, Component.literal("Hello Test"));
|
||||||
//return (parent) -> new MainScreen(parent);
|
//return (parent) -> new MainScreen(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,9 @@ import org.betterx.ui.layout.values.Alignment;
|
||||||
import org.betterx.ui.layout.values.DynamicSize;
|
import org.betterx.ui.layout.values.DynamicSize;
|
||||||
import org.betterx.ui.layout.values.Rectangle;
|
import org.betterx.ui.layout.values.Rectangle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||||
|
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
|
@ -65,6 +67,21 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
|
||||||
return height.calculatedSize();
|
return height.calculatedSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final void setClippingRect(Rectangle clippingRect) {
|
||||||
|
if (clippingRect == null) {
|
||||||
|
RenderSystem.disableScissor();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final double uiScale = Minecraft.getInstance().getWindow().getGuiScale();
|
||||||
|
final int windowHeight = Minecraft.getInstance().getWindow().getHeight();
|
||||||
|
RenderSystem.enableScissor(
|
||||||
|
(int) (clippingRect.left * uiScale),
|
||||||
|
(int) (windowHeight - clippingRect.bottom() * uiScale),
|
||||||
|
(int) (clippingRect.width * uiScale),
|
||||||
|
(int) (clippingRect.height * uiScale)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public void render(PoseStack poseStack, int x, int y, float a, Rectangle parentBounds, Rectangle clipRect) {
|
public void render(PoseStack poseStack, int x, int y, float a, Rectangle parentBounds, Rectangle clipRect) {
|
||||||
Rectangle r = relativeBounds.movedBy(parentBounds.left, parentBounds.top);
|
Rectangle r = relativeBounds.movedBy(parentBounds.left, parentBounds.top);
|
||||||
Rectangle clip = r.intersect(clipRect);
|
Rectangle clip = r.intersect(clipRect);
|
||||||
|
@ -85,7 +102,9 @@ public abstract class Component<R extends ComponentRenderer> implements Componen
|
||||||
Rectangle clipRect
|
Rectangle clipRect
|
||||||
) {
|
) {
|
||||||
if (renderer != null) {
|
if (renderer != null) {
|
||||||
|
setClippingRect(clipRect);
|
||||||
renderer.renderInBounds(poseStack, x, y, a, renderBounds, clipRect);
|
renderer.renderInBounds(poseStack, x, y, a, renderBounds, clipRect);
|
||||||
|
setClippingRect(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,11 +68,11 @@ public class HorizontalStack extends AbstractStack<NullRenderer, HorizontalStack
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HorizontalStack centered(Component<?> c) {
|
public static HorizontalStack centered(Component<?> c) {
|
||||||
return new HorizontalStack(DynamicSize.relative(1), DynamicSize.relative(1)).addFiller().add(c).addFiller();
|
return new HorizontalStack(DynamicSize.fill(), DynamicSize.fill()).addFiller().add(c).addFiller();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HorizontalStack bottom(Component<?> c) {
|
public static HorizontalStack bottom(Component<?> c) {
|
||||||
return new HorizontalStack(DynamicSize.relative(1), DynamicSize.relative(1)).add(c).addFiller();
|
return new HorizontalStack(DynamicSize.fill(), DynamicSize.fill()).add(c).addFiller();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HorizontalStack addEmpty(DynamicSize size) {
|
protected HorizontalStack addEmpty(DynamicSize size) {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.betterx.ui.layout.components;
|
package org.betterx.ui.layout.components;
|
||||||
|
|
||||||
import org.betterx.ui.layout.components.render.ComponentRenderer;
|
import org.betterx.ui.layout.components.render.ComponentRenderer;
|
||||||
|
import org.betterx.ui.layout.components.render.NullRenderer;
|
||||||
import org.betterx.ui.layout.components.render.ScrollerRenderer;
|
import org.betterx.ui.layout.components.render.ScrollerRenderer;
|
||||||
import org.betterx.ui.layout.values.DynamicSize;
|
import org.betterx.ui.layout.values.DynamicSize;
|
||||||
import org.betterx.ui.layout.values.Rectangle;
|
import org.betterx.ui.layout.values.Rectangle;
|
||||||
|
import org.betterx.ui.vanilla.VanillaScrollerRenderer;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
@ -30,6 +32,25 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
|
||||||
this.scrollerRenderer = scrollerRenderer;
|
this.scrollerRenderer = scrollerRenderer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VerticalScroll<NullRenderer, VanillaScrollerRenderer> create(Component<?> c) {
|
||||||
|
return create(DynamicSize.relative(1), DynamicSize.relative(1), c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VerticalScroll<NullRenderer, VanillaScrollerRenderer> create(
|
||||||
|
DynamicSize width,
|
||||||
|
DynamicSize height,
|
||||||
|
Component<?> c
|
||||||
|
) {
|
||||||
|
VerticalScroll<NullRenderer, VanillaScrollerRenderer> res = new VerticalScroll<>(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
VanillaScrollerRenderer.DEFAULT,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
res.setChild(c);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
public void setChild(Component<?> c) {
|
public void setChild(Component<?> c) {
|
||||||
this.child = c;
|
this.child = c;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +59,7 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
|
||||||
protected int updateContainerWidth(int containerWidth) {
|
protected int updateContainerWidth(int containerWidth) {
|
||||||
int myWidth = width.calculateOrFill(containerWidth);
|
int myWidth = width.calculateOrFill(containerWidth);
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
child.width.calculateOrFill(myWidth);
|
child.width.calculateOrFill(myWidth - (scrollerRenderer.scrollerPadding() + scrollerRenderer.scrollerWidth()));
|
||||||
child.updateContainerWidth(child.width.calculatedSize());
|
child.updateContainerWidth(child.width.calculatedSize());
|
||||||
}
|
}
|
||||||
return myWidth;
|
return myWidth;
|
||||||
|
@ -56,7 +77,9 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getContentWidth() {
|
public int getContentWidth() {
|
||||||
return child != null ? child.getContentWidth() : 0;
|
return scrollerRenderer.scrollerWidth() + scrollerRenderer.scrollerPadding() + (child != null
|
||||||
|
? child.getContentWidth()
|
||||||
|
: 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,11 +110,21 @@ public class VerticalScroll<R extends ComponentRenderer, RS extends ScrollerRend
|
||||||
|
|
||||||
if (showScrollBar()) {
|
if (showScrollBar()) {
|
||||||
if (child != null) {
|
if (child != null) {
|
||||||
|
poseStack.pushPose();
|
||||||
|
poseStack.translate(0, scrollerOffset(), 0);
|
||||||
|
setClippingRect(clipRect);
|
||||||
child.render(
|
child.render(
|
||||||
poseStack, x, y, a,
|
poseStack, x, y, a,
|
||||||
renderBounds.movedBy(0, scrollerOffset(), scrollerRenderer.scrollerWidth(), 0),
|
renderBounds.movedBy(
|
||||||
|
0,
|
||||||
|
scrollerOffset(),
|
||||||
|
scrollerRenderer.scrollerWidth() + scrollerRenderer.scrollerPadding(),
|
||||||
|
0
|
||||||
|
),
|
||||||
clipRect
|
clipRect
|
||||||
);
|
);
|
||||||
|
setClippingRect(null);
|
||||||
|
poseStack.popPose();
|
||||||
}
|
}
|
||||||
scrollerRenderer.renderScrollBar(renderBounds, saveScrollerY(), scrollerHeight);
|
scrollerRenderer.renderScrollBar(renderBounds, saveScrollerY(), scrollerHeight);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,7 +12,11 @@ public interface ScrollerRenderer {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
default int scrollerWidth() {
|
default int scrollerWidth() {
|
||||||
return 16;
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
default int scrollerPadding() {
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
default Rectangle getScrollerBounds(Rectangle renderBounds) {
|
default Rectangle getScrollerBounds(Rectangle renderBounds) {
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class DynamicSize {
|
||||||
public int calculateOrFill(int parentSize) {
|
public int calculateOrFill(int parentSize) {
|
||||||
calculatedSize = calculate(parentSize);
|
calculatedSize = calculate(parentSize);
|
||||||
if (sizeType instanceof SizeType.Fill || sizeType instanceof SizeType.FitContentOrFill) {
|
if (sizeType instanceof SizeType.Fill || sizeType instanceof SizeType.FitContentOrFill) {
|
||||||
calculatedSize = parentSize;
|
calculatedSize = Math.max(parentSize, calculatedSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return calculatedSize;
|
return calculatedSize;
|
||||||
|
|
|
@ -26,6 +26,10 @@ public class Rectangle {
|
||||||
return top + height;
|
return top + height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Size size() {
|
||||||
|
return new Size(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
public Rectangle movedBy(int left, int top) {
|
public Rectangle movedBy(int left, int top) {
|
||||||
return new Rectangle(this.left + left, this.top + top, this.width, this.height);
|
return new Rectangle(this.left + left, this.top + top, this.width, this.height);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.betterx.ui.vanilla;
|
package org.betterx.ui.vanilla;
|
||||||
|
|
||||||
import org.betterx.ui.layout.components.Panel;
|
import org.betterx.ui.layout.components.Panel;
|
||||||
|
import org.betterx.ui.layout.components.Text;
|
||||||
import org.betterx.ui.layout.components.VerticalStack;
|
import org.betterx.ui.layout.components.VerticalStack;
|
||||||
import org.betterx.ui.layout.values.DynamicSize;
|
import org.betterx.ui.layout.values.DynamicSize;
|
||||||
|
|
||||||
|
@ -45,16 +46,19 @@ public abstract class LayoutScreen extends Screen {
|
||||||
protected org.betterx.ui.layout.components.Component<?> addTitle(org.betterx.ui.layout.components.Component<?> content) {
|
protected org.betterx.ui.layout.components.Component<?> addTitle(org.betterx.ui.layout.components.Component<?> content) {
|
||||||
VerticalStack rows = new VerticalStack(DynamicSize.relative(1), DynamicSize.relative(1));
|
VerticalStack rows = new VerticalStack(DynamicSize.relative(1), DynamicSize.relative(1));
|
||||||
|
|
||||||
//rows.add(this.title, GridLayout.Alignment.CENTER, this);
|
rows.add(new Text(DynamicSize.fill(), DynamicSize.fit(), title).centerHorizontal());
|
||||||
rows.addSpacer(15);
|
rows.addSpacer(15);
|
||||||
rows.add(content);
|
rows.add(content);
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void renderBackground(PoseStack poseStack, int i, int j, float f) {
|
||||||
|
renderDirtBackground(i);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(PoseStack poseStack, int i, int j, float f) {
|
public void render(PoseStack poseStack, int i, int j, float f) {
|
||||||
renderDirtBackground(i);
|
renderBackground(poseStack, i, j, f);
|
||||||
super.render(poseStack, i, j, f);
|
super.render(poseStack, i, j, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package org.betterx.ui.vanilla;
|
||||||
|
|
||||||
|
import org.betterx.ui.layout.components.render.ScrollerRenderer;
|
||||||
|
import org.betterx.ui.layout.values.Rectangle;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
|
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
|
||||||
|
import com.mojang.blaze3d.vertex.Tesselator;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexFormat;
|
||||||
|
import net.minecraft.client.renderer.GameRenderer;
|
||||||
|
|
||||||
|
public class VanillaScrollerRenderer implements ScrollerRenderer {
|
||||||
|
public static final VanillaScrollerRenderer DEFAULT = new VanillaScrollerRenderer();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderScrollBar(Rectangle b, int pickerOffset, int pickerSize) {
|
||||||
|
b = this.getScrollerBounds(b);
|
||||||
|
Rectangle p = this.getPickerBounds(b, pickerOffset, pickerSize);
|
||||||
|
|
||||||
|
|
||||||
|
Tesselator tesselator = Tesselator.getInstance();
|
||||||
|
BufferBuilder bufferBuilder = tesselator.getBuilder();
|
||||||
|
RenderSystem.disableTexture();
|
||||||
|
RenderSystem.setShader(GameRenderer::getPositionColorShader);
|
||||||
|
|
||||||
|
|
||||||
|
bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
|
||||||
|
|
||||||
|
//scroller background
|
||||||
|
bufferBuilder.vertex(b.left, b.bottom(), 0.0D).color(0, 0, 0, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(b.right(), b.bottom(), 0.0D).color(0, 0, 0, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(b.right(), b.top, 0.0D).color(0, 0, 0, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(b.left, b.top, 0.0D).color(0, 0, 0, 255).endVertex();
|
||||||
|
|
||||||
|
//scroll widget shadow
|
||||||
|
bufferBuilder.vertex(p.left, p.bottom(), 0.0D).color(128, 128, 128, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(p.right(), p.bottom(), 0.0D).color(128, 128, 128, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(p.right(), p.top, 0.0D).color(128, 128, 128, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(p.left, p.top, 0.0D).color(128, 128, 128, 255).endVertex();
|
||||||
|
|
||||||
|
//scroll widget
|
||||||
|
bufferBuilder.vertex(p.left, p.bottom() - 1, 0.0D)
|
||||||
|
.color(192, 192, 192, 255)
|
||||||
|
.endVertex();
|
||||||
|
bufferBuilder.vertex(p.right() - 1, p.bottom() - 1, 0.0D)
|
||||||
|
.color(192, 192, 192, 255)
|
||||||
|
.endVertex();
|
||||||
|
bufferBuilder.vertex(p.right() - 1, p.top, 0.0D).color(192, 192, 192, 255).endVertex();
|
||||||
|
bufferBuilder.vertex(p.left, p.top, 0.0D).color(192, 192, 192, 255).endVertex();
|
||||||
|
|
||||||
|
tesselator.end();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue