mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Refactor how components are handled.
- A `GuiComponent` now is union of pointers. - `pos` and `size` are now stored in each component rather than being passed down through functions.
This commit is contained in:
parent
fd0e903431
commit
5c74694611
@ -1,111 +0,0 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const vec = main.vec;
|
||||
const Vec2f = vec.Vec2f;
|
||||
|
||||
pub const Button = @import("components/Button.zig");
|
||||
pub const CheckBox = @import("components/CheckBox.zig");
|
||||
pub const Label = @import("components/Label.zig");
|
||||
pub const Slider = @import("components/Slider.zig");
|
||||
pub const ScrollBar = @import("components/ScrollBar.zig");
|
||||
pub const TextInput = @import("components/TextInput.zig");
|
||||
pub const VerticalList = @import("components/VerticalList.zig");
|
||||
|
||||
const GuiComponent = @This();
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
impl: Impl,
|
||||
|
||||
const Impl = union(enum) {
|
||||
button: Button,
|
||||
checkBox: CheckBox,
|
||||
label: Label,
|
||||
scrollBar: ScrollBar,
|
||||
slider: Slider,
|
||||
textInput: TextInput,
|
||||
verticalList: VerticalList,
|
||||
};
|
||||
|
||||
pub fn deinit(self: *GuiComponent) void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "deinit")) {
|
||||
impl.deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateSelected(self: *GuiComponent) void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "updateSelected")) {
|
||||
impl.updateSelected(self.pos, self.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "updateHovered")) {
|
||||
impl.updateHovered(self.pos, self.size, mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *GuiComponent, mousePosition: Vec2f) !void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "render")) {
|
||||
try impl.render(self.pos, self.size, mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "mainButtonPressed")) {
|
||||
impl.mainButtonPressed(self.pos, self.size, mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.impl) {
|
||||
inline else => |*impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "mainButtonReleased")) {
|
||||
impl.mainButtonReleased(self.pos, self.size, mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(pos: Vec2f, size: Vec2f, point: Vec2f) bool {
|
||||
return @reduce(.And, point >= pos) and @reduce(.And, point < pos + size);
|
||||
}
|
@ -119,7 +119,7 @@ pub fn mainButtonPressed(self: *const GuiWindow, mousePosition: Vec2f) void {
|
||||
} else {
|
||||
var selectedComponent: ?*GuiComponent = null;
|
||||
for(self.components) |*component| {
|
||||
if(GuiComponent.contains(component.pos, component.size, scaledMousePos)) {
|
||||
if(GuiComponent.contains(component.pos(), component.size(), scaledMousePos)) {
|
||||
selectedComponent = component;
|
||||
}
|
||||
}
|
||||
@ -326,7 +326,7 @@ pub fn updateHovered(self: *GuiWindow, mousePosition: Vec2f) !void {
|
||||
while(i != 0) {
|
||||
i -= 1;
|
||||
const component = &self.components[i];
|
||||
if(GuiComponent.contains(component.pos, component.size, (mousePosition - self.pos)/@splat(2, self.scale))) {
|
||||
if(GuiComponent.contains(component.pos(), component.size(), (mousePosition - self.pos)/@splat(2, self.scale))) {
|
||||
component.updateHovered((mousePosition - self.pos)/@splat(2, self.scale));
|
||||
break;
|
||||
}
|
||||
|
@ -32,11 +32,12 @@ pub var buttonUniforms: struct {
|
||||
pressed: c_int,
|
||||
} = undefined;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
pressed: bool = false,
|
||||
hovered: bool = false,
|
||||
onAction: *const fn() void,
|
||||
textSize: Vec2f,
|
||||
label: Label,
|
||||
label: *Label,
|
||||
|
||||
pub fn __init() !void {
|
||||
shader = try Shader.initAndGetUniforms("assets/cubyz/shaders/ui/button.vs", "assets/cubyz/shaders/ui/button.fs", &buttonUniforms);
|
||||
@ -52,42 +53,47 @@ pub fn __deinit() void {
|
||||
|
||||
fn defaultOnAction() void {}
|
||||
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, onAction: ?*const fn() void) Allocator.Error!GuiComponent {
|
||||
const labelComponent = try Label.init(undefined, width - 3*border, text, .center);
|
||||
var self = Button {
|
||||
.onAction = if(onAction) |a| a else &defaultOnAction,
|
||||
.label = labelComponent.impl.label,
|
||||
.textSize = labelComponent.size,
|
||||
};
|
||||
return GuiComponent {
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, onAction: ?*const fn() void) Allocator.Error!*Button {
|
||||
const label = try Label.init(undefined, width - 3*border, text, .center);
|
||||
const self = try gui.allocator.create(Button);
|
||||
self.* = Button {
|
||||
.pos = pos,
|
||||
.size = Vec2f{width, self.textSize[1] + 3*border},
|
||||
.impl = .{.button = self}
|
||||
.size = Vec2f{width, label.size[1] + 3*border},
|
||||
.onAction = if(onAction) |a| a else &defaultOnAction,
|
||||
.label = label,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const Button) void {
|
||||
self.label.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *Button) GuiComponent {
|
||||
return GuiComponent {
|
||||
.button = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Button) void {
|
||||
self.label.deinit();
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *Button, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
pub fn updateHovered(self: *Button, _: Vec2f) void {
|
||||
self.hovered = true;
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *Button, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
pub fn mainButtonPressed(self: *Button, _: Vec2f) void {
|
||||
self.pressed = true;
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *Button, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonReleased(self: *Button, mousePosition: Vec2f) void {
|
||||
if(self.pressed) {
|
||||
self.pressed = false;
|
||||
if(GuiComponent.contains(pos, size, mousePosition)) {
|
||||
if(GuiComponent.contains(self.pos, self.size, mousePosition)) {
|
||||
self.onAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *Button, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !void {
|
||||
pub fn render(self: *Button, mousePosition: Vec2f) !void {
|
||||
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
|
||||
texture.bind();
|
||||
shader.bind();
|
||||
@ -95,14 +101,15 @@ pub fn render(self: *Button, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !voi
|
||||
if(self.pressed) {
|
||||
draw.setColor(0xff000000);
|
||||
graphics.c.glUniform1i(buttonUniforms.pressed, 1);
|
||||
} else if(GuiComponent.contains(pos, size, mousePosition) and self.hovered) {
|
||||
} else if(GuiComponent.contains(self.pos, self.size, mousePosition) and self.hovered) {
|
||||
draw.setColor(0xff000040);
|
||||
} else {
|
||||
draw.setColor(0xff000000);
|
||||
}
|
||||
self.hovered = false;
|
||||
draw.customShadedRect(buttonUniforms, pos, size);
|
||||
draw.customShadedRect(buttonUniforms, self.pos, self.size);
|
||||
graphics.c.glUniform1i(buttonUniforms.pressed, 0);
|
||||
const textPos = pos + size/@splat(2, @as(f32, 2.0)) - self.textSize/@splat(2, @as(f32, 2.0));
|
||||
try self.label.render(textPos, self.textSize, mousePosition - textPos);
|
||||
const textPos = self.pos + self.size/@splat(2, @as(f32, 2.0)) - self.label.size/@splat(2, @as(f32, 2.0));
|
||||
self.label.pos = textPos;
|
||||
try self.label.render(mousePosition - self.pos);
|
||||
}
|
@ -25,12 +25,13 @@ const boxSize: f32 = 16;
|
||||
var textureChecked: Texture = undefined;
|
||||
var textureEmpty: Texture = undefined;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
state: bool = false,
|
||||
pressed: bool = false,
|
||||
hovered: bool = false,
|
||||
onAction: *const fn(bool) void,
|
||||
textSize: Vec2f,
|
||||
label: Label,
|
||||
label: *Label,
|
||||
|
||||
pub fn __init() !void {
|
||||
textureChecked = try Texture.initFromFile("assets/cubyz/ui/checked_box.png");
|
||||
@ -42,44 +43,49 @@ pub fn __deinit() void {
|
||||
textureEmpty.deinit();
|
||||
}
|
||||
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, initialValue: bool, onAction: *const fn(bool) void) Allocator.Error!GuiComponent {
|
||||
const labelComponent = try Label.init(undefined, width - 3*border - boxSize, text, .left);
|
||||
var self = CheckBox {
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, initialValue: bool, onAction: *const fn(bool) void) Allocator.Error!*CheckBox {
|
||||
const label = (try Label.init(undefined, width - 3*border - boxSize, text, .left));
|
||||
const self = try gui.allocator.create(CheckBox);
|
||||
self.* = CheckBox {
|
||||
.pos = pos,
|
||||
.size = Vec2f{@max(width, label.size[0] + 3*border + boxSize), label.size[1] + 3*border},
|
||||
.state = initialValue,
|
||||
.onAction = onAction,
|
||||
.label = labelComponent.impl.label,
|
||||
.textSize = labelComponent.size,
|
||||
};
|
||||
return GuiComponent {
|
||||
.pos = pos,
|
||||
.size = Vec2f{@max(width, self.textSize[0] + 3*border + boxSize), self.textSize[1] + 3*border},
|
||||
.impl = .{.checkBox = self}
|
||||
.label = label,
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: CheckBox) void {
|
||||
pub fn deinit(self: *const CheckBox) void {
|
||||
self.label.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *CheckBox, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
pub fn toComponent(self: *CheckBox) GuiComponent {
|
||||
return GuiComponent {
|
||||
.checkBox = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *CheckBox, _: Vec2f) void {
|
||||
self.hovered = true;
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *CheckBox, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
pub fn mainButtonPressed(self: *CheckBox, _: Vec2f) void {
|
||||
self.pressed = true;
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *CheckBox, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonReleased(self: *CheckBox, mousePosition: Vec2f) void {
|
||||
if(self.pressed) {
|
||||
self.pressed = false;
|
||||
if(GuiComponent.contains(pos, size, mousePosition)) {
|
||||
if(GuiComponent.contains(self.pos, self.size, mousePosition)) {
|
||||
self.state = !self.state;
|
||||
self.onAction(self.state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *CheckBox, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !void {
|
||||
pub fn render(self: *CheckBox, mousePosition: Vec2f) !void {
|
||||
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
|
||||
if(self.state) {
|
||||
textureChecked.bind();
|
||||
@ -91,14 +97,15 @@ pub fn render(self: *CheckBox, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !v
|
||||
if(self.pressed) {
|
||||
draw.setColor(0xff000000);
|
||||
graphics.c.glUniform1i(Button.buttonUniforms.pressed, 1);
|
||||
} else if(GuiComponent.contains(pos, size, mousePosition) and self.hovered) {
|
||||
} else if(GuiComponent.contains(self.pos, self.size, mousePosition) and self.hovered) {
|
||||
draw.setColor(0xff000040);
|
||||
} else {
|
||||
draw.setColor(0xff000000);
|
||||
}
|
||||
self.hovered = false;
|
||||
draw.customShadedRect(Button.buttonUniforms, pos + Vec2f{0, size[1]/2 - boxSize/2}, @splat(2, boxSize));
|
||||
draw.customShadedRect(Button.buttonUniforms, self.pos + Vec2f{0, self.size[1]/2 - boxSize/2}, @splat(2, boxSize));
|
||||
graphics.c.glUniform1i(Button.buttonUniforms.pressed, 0);
|
||||
const textPos = pos + Vec2f{boxSize/2, 0} + size/@splat(2, @as(f32, 2.0)) - self.textSize/@splat(2, @as(f32, 2.0));
|
||||
try self.label.render(textPos, self.textSize, mousePosition - textPos);
|
||||
const textPos = self.pos + Vec2f{boxSize/2, 0} + self.size/@splat(2, @as(f32, 2.0)) - self.label.size/@splat(2, @as(f32, 2.0));
|
||||
self.label.pos = textPos;
|
||||
try self.label.render(mousePosition - textPos);
|
||||
}
|
@ -15,25 +15,39 @@ const Label = @This();
|
||||
|
||||
const fontSize: f32 = 16;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
text: TextBuffer,
|
||||
textSize: Vec2f = undefined,
|
||||
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, text: []const u8, alignment: TextBuffer.Alignment) Allocator.Error!GuiComponent {
|
||||
var self = Label {
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, text: []const u8, alignment: TextBuffer.Alignment) Allocator.Error!*Label {
|
||||
const self = try gui.allocator.create(Label);
|
||||
self.* = Label {
|
||||
.text = try TextBuffer.init(gui.allocator, text, .{}, false, alignment),
|
||||
};
|
||||
self.textSize = try self.text.calculateLineBreaks(fontSize, maxWidth);
|
||||
return GuiComponent {
|
||||
.pos = pos,
|
||||
.size = self.textSize,
|
||||
.impl = .{.label = self}
|
||||
.size = undefined,
|
||||
};
|
||||
self.size = try self.text.calculateLineBreaks(fontSize, maxWidth);
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const Label) void {
|
||||
self.text.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *Label) GuiComponent {
|
||||
return GuiComponent{
|
||||
.label = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Label) void {
|
||||
pub fn updateText(self: *Label, newText: []const u8) !void {
|
||||
const alignment = self.text.alignment;
|
||||
self.text.deinit();
|
||||
self.text = try TextBuffer.init(gui.allocator, newText, .{}, false, alignment);
|
||||
self.size = try self.text.calculateLineBreaks(fontSize, self.size[0]);
|
||||
}
|
||||
|
||||
pub fn render(self: *Label, pos: Vec2f, _: Vec2f, _: Vec2f) !void {
|
||||
try self.text.render(pos[0], pos[1], fontSize);
|
||||
pub fn render(self: *Label, _: Vec2f) !void {
|
||||
try self.text.render(self.pos[0], self.pos[1], fontSize);
|
||||
}
|
@ -22,10 +22,10 @@ const fontSize: f32 = 16;
|
||||
|
||||
var texture: Texture = undefined;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
currentState: f32,
|
||||
button: Button,
|
||||
buttonSize: Vec2f,
|
||||
buttonPos: Vec2f = .{0, 0},
|
||||
button: *Button,
|
||||
mouseAnchor: f32 = undefined,
|
||||
|
||||
pub fn __init() !void {
|
||||
@ -36,34 +36,39 @@ pub fn __deinit() void {
|
||||
texture.deinit();
|
||||
}
|
||||
|
||||
pub fn init(pos: Vec2f, width: f32, height: f32, initialState: f32) Allocator.Error!GuiComponent {
|
||||
const buttonComponent = try Button.init(undefined, undefined, "", null);
|
||||
var self = ScrollBar {
|
||||
.currentState = initialState,
|
||||
.button = buttonComponent.impl.button,
|
||||
.buttonSize = .{width, 16},
|
||||
};
|
||||
const size = Vec2f{width, height};
|
||||
self.setButtonPosFromValue(size);
|
||||
return GuiComponent {
|
||||
pub fn init(pos: Vec2f, width: f32, height: f32, initialState: f32) Allocator.Error!*ScrollBar {
|
||||
const button = try Button.init(.{0, 0}, undefined, "", null);
|
||||
const self = try gui.allocator.create(ScrollBar);
|
||||
self.* = ScrollBar {
|
||||
.pos = pos,
|
||||
.size = size,
|
||||
.impl = .{.scrollBar = self}
|
||||
.size = Vec2f{width, height},
|
||||
.currentState = initialState,
|
||||
.button = button,
|
||||
};
|
||||
self.button.size = .{width, 16};
|
||||
self.setButtonPosFromValue();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *const ScrollBar) void {
|
||||
self.button.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *ScrollBar) GuiComponent {
|
||||
return GuiComponent{
|
||||
.scrollBar = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: ScrollBar) void {
|
||||
self.button.deinit();
|
||||
fn setButtonPosFromValue(self: *ScrollBar) void {
|
||||
const range: f32 = self.size[1] - self.button.size[1];
|
||||
self.button.pos[1] = range*self.currentState;
|
||||
}
|
||||
|
||||
fn setButtonPosFromValue(self: *ScrollBar, size: Vec2f) void {
|
||||
const range: f32 = size[1] - self.buttonSize[1];
|
||||
self.buttonPos[1] = range*self.currentState;
|
||||
}
|
||||
|
||||
fn updateValueFromButtonPos(self: *ScrollBar, size: Vec2f) void {
|
||||
const range: f32 = size[1] - self.buttonSize[1];
|
||||
const value = self.buttonPos[1]/range;
|
||||
fn updateValueFromButtonPos(self: *ScrollBar) void {
|
||||
const range: f32 = self.size[1] - self.button.size[1];
|
||||
const value = self.button.pos[1]/range;
|
||||
if(value != self.currentState) {
|
||||
self.currentState = value;
|
||||
}
|
||||
@ -74,36 +79,38 @@ pub fn scroll(self: *ScrollBar, offset: f32) void {
|
||||
self.currentState = @min(1, @max(0, self.currentState));
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *ScrollBar, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.buttonPos + pos, self.buttonSize, mousePosition)) {
|
||||
self.button.updateHovered(self.buttonPos, self.buttonSize, mousePosition - pos);
|
||||
pub fn updateHovered(self: *ScrollBar, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.button.pos, self.button.size, mousePosition - self.pos)) {
|
||||
self.button.updateHovered(mousePosition - self.pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *ScrollBar, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.buttonPos, self.buttonSize, mousePosition - pos)) {
|
||||
self.button.mainButtonPressed(self.buttonPos, self.buttonSize, mousePosition - pos);
|
||||
self.mouseAnchor = mousePosition[1] - self.buttonPos[1];
|
||||
pub fn mainButtonPressed(self: *ScrollBar, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.button.pos, self.button.size, mousePosition - self.pos)) {
|
||||
self.button.mainButtonPressed(mousePosition - self.pos);
|
||||
self.mouseAnchor = mousePosition[1] - self.button.pos[1];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *ScrollBar, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
self.button.mainButtonReleased(undefined, undefined, undefined);
|
||||
pub fn mainButtonReleased(self: *ScrollBar, mousePosition: Vec2f) void {
|
||||
self.button.mainButtonReleased(mousePosition - self.pos);
|
||||
}
|
||||
|
||||
pub fn render(self: *ScrollBar, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !void {
|
||||
pub fn render(self: *ScrollBar, mousePosition: Vec2f) !void {
|
||||
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
|
||||
texture.bind();
|
||||
Button.shader.bind();
|
||||
draw.setColor(0xff000000);
|
||||
draw.customShadedRect(Button.buttonUniforms, pos, size);
|
||||
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);
|
||||
|
||||
const range: f32 = size[1] - self.buttonSize[1];
|
||||
self.setButtonPosFromValue(size);
|
||||
const range: f32 = self.size[1] - self.button.size[1];
|
||||
self.setButtonPosFromValue();
|
||||
if(self.button.pressed) {
|
||||
self.buttonPos[1] = mousePosition[1] - self.mouseAnchor;
|
||||
self.buttonPos[1] = @min(@max(self.buttonPos[1], 0), range - 0.001);
|
||||
self.updateValueFromButtonPos(size);
|
||||
self.button.pos[1] = mousePosition[1] - self.mouseAnchor;
|
||||
self.button.pos[1] = @min(@max(self.button.pos[1], 0), range - 0.001);
|
||||
self.updateValueFromButtonPos();
|
||||
}
|
||||
try self.button.render(pos + self.buttonPos, self.buttonSize, mousePosition);
|
||||
const oldTranslation = draw.setTranslation(self.pos);
|
||||
defer draw.restoreTranslation(oldTranslation);
|
||||
try self.button.render(mousePosition - self.pos);
|
||||
}
|
@ -23,16 +23,15 @@ const fontSize: f32 = 16;
|
||||
|
||||
var texture: Texture = undefined;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
callback: *const fn(u16) void,
|
||||
currentSelection: u16,
|
||||
text: []const u8,
|
||||
currentText: []u8,
|
||||
values: [][]const u8,
|
||||
label: Label,
|
||||
labelSize: Vec2f,
|
||||
button: Button,
|
||||
buttonSize: Vec2f,
|
||||
buttonPos: Vec2f = .{0, 0},
|
||||
label: *Label,
|
||||
button: *Button,
|
||||
mouseAnchor: f32 = undefined,
|
||||
|
||||
pub fn __init() !void {
|
||||
@ -43,7 +42,7 @@ pub fn __deinit() void {
|
||||
texture.deinit();
|
||||
}
|
||||
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, comptime fmt: []const u8, valueList: anytype, initialValue: u16, callback: *const fn(u16) void) Allocator.Error!GuiComponent {
|
||||
pub fn init(pos: Vec2f, width: f32, text: []const u8, comptime fmt: []const u8, valueList: anytype, initialValue: u16, callback: *const fn(u16) void) Allocator.Error!*Slider {
|
||||
var values = try gui.allocator.alloc([]const u8, valueList.len);
|
||||
var maxLen: usize = 0;
|
||||
for(valueList, 0..) |value, i| {
|
||||
@ -54,30 +53,28 @@ pub fn init(pos: Vec2f, width: f32, text: []const u8, comptime fmt: []const u8,
|
||||
const initialText = try gui.allocator.alloc(u8, text.len + maxLen);
|
||||
std.mem.copy(u8, initialText, text);
|
||||
std.mem.set(u8, initialText[text.len..], ' ');
|
||||
const labelComponent = try Label.init(undefined, width - 3*border, initialText, .center);
|
||||
const buttonComponent = try Button.init(undefined, undefined, "", null);
|
||||
var self = Slider {
|
||||
const label = try Label.init(undefined, width - 3*border, initialText, .center);
|
||||
const button = try Button.init(.{0, 0}, undefined, "", null);
|
||||
const self = try gui.allocator.create(Slider);
|
||||
self.* = Slider {
|
||||
.pos = pos,
|
||||
.size = undefined,
|
||||
.callback = callback,
|
||||
.currentSelection = initialValue,
|
||||
.text = text,
|
||||
.currentText = initialText,
|
||||
.label = labelComponent.impl.label,
|
||||
.button = buttonComponent.impl.button,
|
||||
.labelSize = labelComponent.size,
|
||||
.buttonSize = .{16, 16},
|
||||
.label = label,
|
||||
.button = button,
|
||||
.values = values,
|
||||
};
|
||||
self.buttonPos[1] = self.labelSize[1] + 3.5*border;
|
||||
const size = Vec2f{@max(width, self.labelSize[0] + 3*border), self.labelSize[1] + self.buttonSize[1] + 5*border};
|
||||
try self.setButtonPosFromValue(size);
|
||||
return GuiComponent {
|
||||
.pos = pos,
|
||||
.size = size,
|
||||
.impl = .{.slider = self}
|
||||
};
|
||||
self.button.size = .{16, 16};
|
||||
self.button.pos[1] = self.label.size[1] + 3.5*border;
|
||||
self.size = Vec2f{@max(width, self.label.size[0] + 3*border), self.label.size[1] + self.button.size[1] + 5*border};
|
||||
try self.setButtonPosFromValue();
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: Slider) void {
|
||||
pub fn deinit(self: *const Slider) void {
|
||||
self.label.deinit();
|
||||
self.button.deinit();
|
||||
for(self.values) |value| {
|
||||
@ -85,12 +82,19 @@ pub fn deinit(self: Slider) void {
|
||||
}
|
||||
gui.allocator.free(self.values);
|
||||
gui.allocator.free(self.currentText);
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
fn setButtonPosFromValue(self: *Slider, size: Vec2f) !void {
|
||||
const range: f32 = size[0] - 3*border - self.buttonSize[0];
|
||||
self.buttonPos[0] = 1.5*border + range*(0.5 + @intToFloat(f32, self.currentSelection))/@intToFloat(f32, self.values.len);
|
||||
try self.updateLabel(self.values[self.currentSelection], size[0]);
|
||||
pub fn toComponent(self: *Slider) GuiComponent {
|
||||
return GuiComponent {
|
||||
.slider = self
|
||||
};
|
||||
}
|
||||
|
||||
fn setButtonPosFromValue(self: *Slider) !void {
|
||||
const range: f32 = self.size[0] - 3*border - self.button.size[0];
|
||||
self.button.pos[0] = 1.5*border + range*(0.5 + @intToFloat(f32, self.currentSelection))/@intToFloat(f32, self.values.len);
|
||||
try self.updateLabel(self.values[self.currentSelection], self.size[0]);
|
||||
}
|
||||
|
||||
fn updateLabel(self: *Slider, newValue: []const u8, width: f32) !void {
|
||||
@ -98,56 +102,58 @@ fn updateLabel(self: *Slider, newValue: []const u8, width: f32) !void {
|
||||
self.currentText = try gui.allocator.alloc(u8, newValue.len + self.text.len);
|
||||
std.mem.copy(u8, self.currentText, self.text);
|
||||
std.mem.copy(u8, self.currentText[self.text.len..], newValue);
|
||||
const labelComponent = try Label.init(undefined, width - 3*border, self.currentText, .center);
|
||||
const label = try Label.init(undefined, width - 3*border, self.currentText, .center);
|
||||
self.label.deinit();
|
||||
self.label = labelComponent.impl.label;
|
||||
self.label = label;
|
||||
}
|
||||
|
||||
fn updateValueFromButtonPos(self: *Slider, size: Vec2f) !void {
|
||||
const range: f32 = size[0] - 3*border - self.buttonSize[0];
|
||||
const selection = @floatToInt(u16, (self.buttonPos[0] - 1.5*border)/range*@intToFloat(f32, self.values.len));
|
||||
fn updateValueFromButtonPos(self: *Slider) !void {
|
||||
const range: f32 = self.size[0] - 3*border - self.button.size[0];
|
||||
const selection = @floatToInt(u16, (self.button.pos[0] - 1.5*border)/range*@intToFloat(f32, self.values.len));
|
||||
if(selection != self.currentSelection) {
|
||||
self.currentSelection = selection;
|
||||
try self.updateLabel(self.values[selection], size[0]);
|
||||
try self.updateLabel(self.values[selection], self.size[0]);
|
||||
self.callback(selection);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *Slider, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.buttonPos + pos, self.buttonSize, mousePosition)) {
|
||||
self.button.updateHovered(self.buttonPos, self.buttonSize, mousePosition - pos);
|
||||
pub fn updateHovered(self: *Slider, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.button.pos, self.button.size, mousePosition - self.pos)) {
|
||||
self.button.updateHovered(mousePosition - self.pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *Slider, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.buttonPos, self.buttonSize, mousePosition - pos)) {
|
||||
self.button.mainButtonPressed(self.buttonPos, self.buttonSize, mousePosition - pos);
|
||||
self.mouseAnchor = mousePosition[0] - self.buttonPos[0];
|
||||
pub fn mainButtonPressed(self: *Slider, mousePosition: Vec2f) void {
|
||||
if(GuiComponent.contains(self.button.pos, self.button.size, mousePosition - self.pos)) {
|
||||
self.button.mainButtonPressed(mousePosition - self.pos);
|
||||
self.mouseAnchor = mousePosition[0] - self.button.pos[0];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *Slider, _: Vec2f, _: Vec2f, _: Vec2f) void {
|
||||
self.button.mainButtonReleased(undefined, undefined, undefined);
|
||||
pub fn mainButtonReleased(self: *Slider, _: Vec2f) void {
|
||||
self.button.mainButtonReleased(undefined);
|
||||
}
|
||||
|
||||
pub fn render(self: *Slider, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !void {
|
||||
pub fn render(self: *Slider, mousePosition: Vec2f) !void {
|
||||
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
|
||||
texture.bind();
|
||||
Button.shader.bind();
|
||||
draw.setColor(0xff000000);
|
||||
draw.customShadedRect(Button.buttonUniforms, pos, size);
|
||||
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);
|
||||
|
||||
const range: f32 = size[0] - 3*border - self.buttonSize[0];
|
||||
const range: f32 = self.size[0] - 3*border - self.button.size[0];
|
||||
draw.setColor(0x80000000);
|
||||
draw.rect(pos + Vec2f{1.5*border + self.buttonSize[0]/2, self.buttonPos[1] + self.buttonSize[1]/2 - border}, .{range, 2*border});
|
||||
draw.rect(self.pos + Vec2f{1.5*border + self.button.size[0]/2, self.button.pos[1] + self.button.size[1]/2 - border}, .{range, 2*border});
|
||||
|
||||
const labelPos = pos + @splat(2, 1.5*border);
|
||||
try self.label.render(labelPos, self.labelSize, mousePosition);
|
||||
self.label.pos = self.pos + @splat(2, 1.5*border);
|
||||
try self.label.render(mousePosition);
|
||||
|
||||
if(self.button.pressed) {
|
||||
self.buttonPos[0] = mousePosition[0] - self.mouseAnchor;
|
||||
self.buttonPos[0] = @min(@max(self.buttonPos[0], 1.5*border), 1.5*border + range - 0.001);
|
||||
try self.updateValueFromButtonPos(size);
|
||||
self.button.pos[0] = mousePosition[0] - self.mouseAnchor;
|
||||
self.button.pos[0] = @min(@max(self.button.pos[0], 1.5*border), 1.5*border + range - 0.001);
|
||||
try self.updateValueFromButtonPos();
|
||||
}
|
||||
try self.button.render(pos + self.buttonPos, self.buttonSize, mousePosition);
|
||||
const oldTranslation = draw.setTranslation(self.pos);
|
||||
defer draw.restoreTranslation(oldTranslation);
|
||||
try self.button.render(mousePosition - self.pos);
|
||||
}
|
@ -22,6 +22,8 @@ const fontSize: f32 = 16;
|
||||
|
||||
var texture: Texture = undefined;
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
pressed: bool = false,
|
||||
cursor: ?u32 = null,
|
||||
selectionStart: ?u32 = null,
|
||||
@ -30,8 +32,7 @@ textBuffer: TextBuffer,
|
||||
maxWidth: f32,
|
||||
maxHeight: f32,
|
||||
textSize: Vec2f = undefined,
|
||||
scrollBar: ScrollBar,
|
||||
scrollBarSize: Vec2f,
|
||||
scrollBar: *ScrollBar,
|
||||
|
||||
pub fn __init() !void {
|
||||
texture = try Texture.initFromFile("assets/cubyz/ui/text_input.png");
|
||||
@ -41,49 +42,54 @@ pub fn __deinit() void {
|
||||
texture.deinit();
|
||||
}
|
||||
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocator.Error!GuiComponent {
|
||||
const scrollBarComponent = try ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0);
|
||||
var self = TextInput {
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocator.Error!*TextInput {
|
||||
const scrollBar = try ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0);
|
||||
const self = try gui.allocator.create(TextInput);
|
||||
self.* = TextInput {
|
||||
.pos = pos,
|
||||
.size = .{maxWidth, maxHeight},
|
||||
.currentString = std.ArrayList(u8).init(gui.allocator),
|
||||
.textBuffer = try TextBuffer.init(gui.allocator, text, .{}, true, .left),
|
||||
.maxWidth = maxWidth,
|
||||
.maxHeight = maxHeight,
|
||||
.scrollBar = scrollBarComponent.impl.scrollBar,
|
||||
.scrollBarSize = scrollBarComponent.size,
|
||||
.scrollBar = scrollBar,
|
||||
};
|
||||
try self.currentString.appendSlice(text);
|
||||
self.textSize = try self.textBuffer.calculateLineBreaks(fontSize, maxWidth - 2*border - scrollBarWidth);
|
||||
return GuiComponent {
|
||||
.pos = pos,
|
||||
.size = .{maxWidth, maxHeight},
|
||||
.impl = .{.textInput = self}
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: TextInput) void {
|
||||
pub fn deinit(self: *const TextInput) void {
|
||||
self.textBuffer.deinit();
|
||||
self.currentString.deinit();
|
||||
self.scrollBar.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *TextInput, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn toComponent(self: *TextInput) GuiComponent {
|
||||
return GuiComponent {
|
||||
.textInput = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *TextInput, mousePosition: Vec2f) void {
|
||||
if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
const diff = self.textSize[1] - (self.maxHeight - 2*border);
|
||||
self.scrollBar.scroll(-main.Window.scrollOffset*32/diff);
|
||||
}
|
||||
if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
const scrollBarPos = Vec2f{size[0] - border - scrollBarWidth, border};
|
||||
if(GuiComponent.contains(scrollBarPos, self.scrollBarSize, mousePosition - pos)) {
|
||||
self.scrollBar.updateHovered(scrollBarPos, self.scrollBarSize, mousePosition - pos);
|
||||
self.scrollBar.pos = Vec2f{self.size[0] - border - scrollBarWidth, border};
|
||||
if(GuiComponent.contains(self.scrollBar.pos, self.scrollBar.size, mousePosition - self.pos)) {
|
||||
self.scrollBar.updateHovered(mousePosition - self.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *TextInput, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonPressed(self: *TextInput, mousePosition: Vec2f) void {
|
||||
if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
const scrollBarPos = Vec2f{size[0] - border - scrollBarWidth, border};
|
||||
if(GuiComponent.contains(scrollBarPos, self.scrollBarSize, mousePosition - pos)) {
|
||||
self.scrollBar.mainButtonPressed(scrollBarPos, self.scrollBarSize, mousePosition - pos);
|
||||
self.scrollBar.pos = Vec2f{self.size[0] - border - scrollBarWidth, border};
|
||||
if(GuiComponent.contains(self.scrollBar.pos, self.scrollBar.size, mousePosition - self.pos)) {
|
||||
self.scrollBar.mainButtonPressed(mousePosition - self.pos);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -93,25 +99,26 @@ pub fn mainButtonPressed(self: *TextInput, pos: Vec2f, size: Vec2f, mousePositio
|
||||
const diff = self.textSize[1] - (self.maxHeight - 2*border);
|
||||
textPos[1] -= diff*self.scrollBar.currentState;
|
||||
}
|
||||
self.selectionStart = self.textBuffer.mousePosToIndex(mousePosition - textPos - pos, self.currentString.items.len);
|
||||
self.selectionStart = self.textBuffer.mousePosToIndex(mousePosition - textPos - self.pos, self.currentString.items.len);
|
||||
self.pressed = true;
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *TextInput, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonReleased(self: *TextInput, mousePosition: Vec2f) void {
|
||||
if(self.pressed) {
|
||||
var textPos = Vec2f{border, border};
|
||||
if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
const diff = self.textSize[1] - (self.maxHeight - 2*border);
|
||||
textPos[1] -= diff*self.scrollBar.currentState;
|
||||
}
|
||||
self.cursor = self.textBuffer.mousePosToIndex(mousePosition - textPos - pos, self.currentString.items.len);
|
||||
self.cursor = self.textBuffer.mousePosToIndex(mousePosition - textPos - self.pos, self.currentString.items.len);
|
||||
if(self.cursor == self.selectionStart) {
|
||||
self.selectionStart = null;
|
||||
}
|
||||
self.pressed = false;
|
||||
gui.setSelectedTextInput(self);
|
||||
} else if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
self.scrollBar.mainButtonReleased(.{size[0] - border - scrollBarWidth, border}, self.scrollBarSize, mousePosition - pos);
|
||||
self.scrollBar.pos = .{self.size[0] - border - scrollBarWidth, border};
|
||||
self.scrollBar.mainButtonReleased(mousePosition - self.pos);
|
||||
gui.setSelectedTextInput(self);
|
||||
}
|
||||
}
|
||||
@ -435,26 +442,27 @@ fn ensureCursorVisibility(self: *TextInput) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *TextInput, pos: Vec2f, size: Vec2f, mousePosition: Vec2f) !void {
|
||||
pub fn render(self: *TextInput, mousePosition: Vec2f) !void {
|
||||
graphics.c.glActiveTexture(graphics.c.GL_TEXTURE0);
|
||||
texture.bind();
|
||||
Button.shader.bind();
|
||||
draw.setColor(0xff000000);
|
||||
draw.customShadedRect(Button.buttonUniforms, pos, size);
|
||||
const oldTranslation = draw.setTranslation(pos);
|
||||
draw.customShadedRect(Button.buttonUniforms, self.pos, self.size);
|
||||
const oldTranslation = draw.setTranslation(self.pos);
|
||||
defer draw.restoreTranslation(oldTranslation);
|
||||
const oldClip = draw.setClip(size);
|
||||
const oldClip = draw.setClip(self.size);
|
||||
defer draw.restoreClip(oldClip);
|
||||
|
||||
var textPos = Vec2f{border, border};
|
||||
if(self.textSize[1] > self.maxHeight - 2*border) {
|
||||
const diff = self.textSize[1] - (self.maxHeight - 2*border);
|
||||
textPos[1] -= diff*self.scrollBar.currentState;
|
||||
try self.scrollBar.render(.{size[0] - self.scrollBarSize[0] - border, border}, self.scrollBarSize, mousePosition - pos);
|
||||
self.scrollBar.pos = .{self.size[0] - self.scrollBar.size[0] - border, border};
|
||||
try self.scrollBar.render(mousePosition - self.pos);
|
||||
}
|
||||
try self.textBuffer.render(textPos[0], textPos[1], fontSize);
|
||||
if(self.pressed) {
|
||||
self.cursor = self.textBuffer.mousePosToIndex(mousePosition - textPos - pos, self.currentString.items.len);
|
||||
self.cursor = self.textBuffer.mousePosToIndex(mousePosition - textPos - self.pos, self.currentString.items.len);
|
||||
}
|
||||
if(self.cursor) |cursor| {
|
||||
var cursorPos = textPos + self.textBuffer.indexToCursorPos(cursor);
|
||||
|
@ -13,80 +13,89 @@ const GuiComponent = gui.GuiComponent;
|
||||
|
||||
const VerticalList = @This();
|
||||
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
children: std.ArrayList(GuiComponent),
|
||||
currentOffset: f32 = 0,
|
||||
maxWidth: f32 = 0,
|
||||
|
||||
pub fn init() Allocator.Error!VerticalList {
|
||||
const self = VerticalList {
|
||||
pub fn init() Allocator.Error!*VerticalList {
|
||||
const self = try gui.allocator.create(VerticalList);
|
||||
self.* = VerticalList {
|
||||
.children = std.ArrayList(GuiComponent).init(gui.allocator),
|
||||
.pos = undefined,
|
||||
.size = .{0, 0},
|
||||
};
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: VerticalList) void {
|
||||
pub fn deinit(self: *const VerticalList) void {
|
||||
for(self.children.items) |*child| {
|
||||
child.deinit();
|
||||
}
|
||||
self.children.deinit();
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *VerticalList, pos: Vec2f) GuiComponent {
|
||||
self.pos = pos;
|
||||
return GuiComponent {
|
||||
.pos = pos,
|
||||
.size = .{self.maxWidth, self.currentOffset},
|
||||
.impl = .{.verticalList = self.*}
|
||||
.verticalList = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add(self: *VerticalList, other: GuiComponent) Allocator.Error!void {
|
||||
pub fn add(self: *VerticalList, _other: anytype) Allocator.Error!void {
|
||||
var other: GuiComponent = undefined;
|
||||
if(@TypeOf(_other) == GuiComponent) {
|
||||
other = _other;
|
||||
} else {
|
||||
other = _other.toComponent();
|
||||
}
|
||||
const added = try self.children.addOne();
|
||||
added.* = other;
|
||||
added.pos[1] += self.currentOffset;
|
||||
self.currentOffset = added.pos[1] + added.size[1];
|
||||
self.maxWidth = @max(self.maxWidth, added.pos[0] + added.size[0]);
|
||||
added.mutPos().*[1] += self.size[1];
|
||||
self.size[1] = added.pos()[1] + added.size()[1];
|
||||
self.size[0] = @max(self.size[0], added.pos()[0] + added.size()[0]);
|
||||
}
|
||||
|
||||
pub fn updateSelected(self: *VerticalList, _: Vec2f, _: Vec2f) void {
|
||||
pub fn updateSelected(self: *VerticalList) void {
|
||||
for(self.children.items) |*child| {
|
||||
child.updateSelected();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *VerticalList, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn updateHovered(self: *VerticalList, mousePosition: Vec2f) void {
|
||||
var i: usize = self.children.items.len;
|
||||
while(i != 0) {
|
||||
i -= 1;
|
||||
const child = &self.children.items[i];
|
||||
if(GuiComponent.contains(child.pos + pos, child.size, mousePosition)) {
|
||||
child.updateHovered(mousePosition - pos);
|
||||
if(GuiComponent.contains(child.pos() + self.pos, child.size(), mousePosition)) {
|
||||
child.updateHovered(mousePosition - self.pos);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *VerticalList, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) anyerror!void { // TODO: Remove anyerror once error union inference works in recursive loops.
|
||||
const oldTranslation = draw.setTranslation(pos);
|
||||
pub fn render(self: *VerticalList, mousePosition: Vec2f) anyerror!void { // TODO: Remove anyerror once error union inference works in recursive loops.
|
||||
const oldTranslation = draw.setTranslation(self.pos);
|
||||
for(self.children.items) |*child| {
|
||||
try child.render(mousePosition - pos);
|
||||
try child.render(mousePosition - self.pos);
|
||||
}
|
||||
draw.restoreTranslation(oldTranslation);
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *VerticalList, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonPressed(self: *VerticalList, mousePosition: Vec2f) void {
|
||||
var selectedChild: ?*GuiComponent = null;
|
||||
for(self.children.items) |*child| {
|
||||
if(GuiComponent.contains(child.pos + pos, child.size, mousePosition)) {
|
||||
if(GuiComponent.contains(child.pos() + self.pos, child.size(), mousePosition)) {
|
||||
selectedChild = child;
|
||||
}
|
||||
}
|
||||
if(selectedChild) |child| {
|
||||
child.mainButtonPressed(mousePosition - pos);
|
||||
child.mainButtonPressed(mousePosition - self.pos);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *VerticalList, pos: Vec2f, _: Vec2f, mousePosition: Vec2f) void {
|
||||
pub fn mainButtonReleased(self: *VerticalList, mousePosition: Vec2f) void {
|
||||
for(self.children.items) |*child| {
|
||||
child.mainButtonReleased(mousePosition - pos);
|
||||
child.mainButtonReleased(mousePosition - self.pos);
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ const CheckBox = @import("components/CheckBox.zig");
|
||||
const ScrollBar = @import("components/ScrollBar.zig");
|
||||
const Slider = @import("components/Slider.zig");
|
||||
const TextInput = @import("components/TextInput.zig");
|
||||
pub const GuiComponent = @import("GuiComponent.zig");
|
||||
pub const GuiComponent = @import("gui_component.zig").GuiComponent;
|
||||
pub const GuiWindow = @import("GuiWindow.zig");
|
||||
|
||||
const windowlist = @import("windows/_windowlist.zig");
|
||||
|
139
src/gui/gui_component.zig
Normal file
139
src/gui/gui_component.zig
Normal file
@ -0,0 +1,139 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("root");
|
||||
const vec = main.vec;
|
||||
const Vec2f = vec.Vec2f;
|
||||
|
||||
pub const GuiComponent = union(enum) {
|
||||
|
||||
pub const Button = @import("components/Button.zig");
|
||||
pub const CheckBox = @import("components/CheckBox.zig");
|
||||
pub const Label = @import("components/Label.zig");
|
||||
pub const Slider = @import("components/Slider.zig");
|
||||
pub const ScrollBar = @import("components/ScrollBar.zig");
|
||||
pub const TextInput = @import("components/TextInput.zig");
|
||||
pub const VerticalList = @import("components/VerticalList.zig");
|
||||
|
||||
|
||||
button: *Button,
|
||||
checkBox: *CheckBox,
|
||||
label: *Label,
|
||||
scrollBar: *ScrollBar,
|
||||
slider: *Slider,
|
||||
textInput: *TextInput,
|
||||
verticalList: *VerticalList,
|
||||
|
||||
pub fn deinit(self: *GuiComponent) void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "deinit")) {
|
||||
impl.deinit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutPos(self: *GuiComponent) *Vec2f {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
return &impl.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mutSize(self: *GuiComponent) *Vec2f {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
return &impl.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pos(self: *GuiComponent) Vec2f {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
return impl.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: *GuiComponent) Vec2f {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
return impl.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateSelected(self: *GuiComponent) void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "updateSelected")) {
|
||||
impl.updateSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "updateHovered")) {
|
||||
impl.updateHovered(mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(self: *GuiComponent, mousePosition: Vec2f) !void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "render")) {
|
||||
try impl.render(mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "mainButtonPressed")) {
|
||||
impl.mainButtonPressed(mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *GuiComponent, mousePosition: Vec2f) void {
|
||||
switch(self.*) {
|
||||
inline else => |impl| {
|
||||
// Only call the function if it exists:
|
||||
inline for(@typeInfo(@TypeOf(impl.*)).Struct.decls) |decl| {
|
||||
if(comptime std.mem.eql(u8, decl.name, "mainButtonReleased")) {
|
||||
impl.mainButtonReleased(mousePosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains(_pos: Vec2f, _size: Vec2f, point: Vec2f) bool {
|
||||
return @reduce(.And, point >= _pos) and @reduce(.And, point < _pos + _size);
|
||||
}
|
||||
};
|
@ -22,12 +22,12 @@ pub var window = GuiWindow {
|
||||
.onCloseFn = &onClose,
|
||||
.components = &components,
|
||||
};
|
||||
var textComponent: *TextInput = undefined;
|
||||
|
||||
const padding: f32 = 8;
|
||||
|
||||
fn apply() void {
|
||||
const oldName = settings.playerName;
|
||||
const textComponent: *TextInput = &components[0].impl.verticalList.children.items[5].impl.textInput;
|
||||
main.globalAllocator.free(settings.playerName);
|
||||
settings.playerName = main.globalAllocator.dupe(u8, textComponent.currentString.items) catch {
|
||||
std.log.err("Encountered out of memory in change_name.apply.", .{});
|
||||
@ -55,10 +55,11 @@ pub fn onOpen() Allocator.Error!void {
|
||||
try list.add(try Label.init(.{0, 16}, width, "\\**italic*\\* \\*\\***bold**\\*\\* \\__underlined_\\_ \\_\\___strike-through__\\_\\_", .center));
|
||||
try list.add(try Label.init(.{0, 16}, width, "Even colors are possible, using the hexadecimal color code:", .center));
|
||||
try list.add(try Label.init(.{0, 16}, width, "\\##ff0000ff#00000000#00000000#ff0000red#000000 \\##ff0000ff#00770077#00000000#ff7700orange#000000 \\##00000000#00ff00ff#00000000#00ff00green#000000 \\##00000000#00000000#0000ffff#0000ffblue", .center));
|
||||
try list.add(try TextInput.init(.{0, 16}, width, 32, "quanturmdoelvloper"));
|
||||
textComponent = try TextInput.init(.{0, 16}, width, 32, "quanturmdoelvloper");
|
||||
try list.add(textComponent);
|
||||
try list.add(try Button.init(.{0, 16}, 100, "Apply", &apply));
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ fn keypressListener(key: c_int, mouseButton: c_int, scancode: c_int) void {
|
||||
|
||||
pub fn onOpen() Allocator.Error!void {
|
||||
var list = try VerticalList.init();
|
||||
list.currentOffset = 8;
|
||||
list.size[1] = 8;
|
||||
inline for(comptime std.meta.fieldNames(@TypeOf(main.keyboard))) |field| {
|
||||
var label = try Label.init(.{0, 8}, 128, field, .left);
|
||||
var button = if(&@field(main.keyboard, field) == selectedKey) (
|
||||
@ -67,7 +67,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
}
|
||||
}
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
try list.add(try CheckBox.init(.{0, 16}, 128, "Bloom", settings.bloom, &bloomCallback));
|
||||
try list.add(try CheckBox.init(.{0, 16}, 128, "Vertical Synchronization", settings.vsync, &vsyncCallback));
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,6 @@ pub var window = GuiWindow {
|
||||
.title = "Hotbar",
|
||||
.id = "cubyz:hotbar",
|
||||
.renderFn = &render,
|
||||
.components = &[_]GuiComponent{},
|
||||
.isHud = true,
|
||||
};
|
||||
|
||||
|
@ -32,7 +32,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
try list.add(try Button.init(.{0, 16}, 128, "Settings", gui.openWindowFunction("cubyz:settings")));
|
||||
try list.add(try Button.init(.{0, 16}, 128, "Exit TODO", &buttonCallbackTest));
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ pub var window = GuiWindow {
|
||||
.components = &components,
|
||||
};
|
||||
|
||||
var ipAddressLabel: *Label = undefined;
|
||||
|
||||
const padding: f32 = 8;
|
||||
|
||||
var connection: ?*ConnectionManager = null;
|
||||
@ -34,9 +36,7 @@ const width: f32 = 420;
|
||||
fn flawedDiscoverIpAddress() !void {
|
||||
connection = try ConnectionManager.init(12347, true); // TODO: default port
|
||||
ipAddress = try std.fmt.allocPrint(main.globalAllocator, "{}", .{connection.?.externalAddress});
|
||||
components[0].impl.verticalList.children.items[1].impl.label.deinit();
|
||||
const labelComponent = try Label.init(undefined, width, ipAddress, .center);
|
||||
components[0].impl.verticalList.children.items[1].impl.label = labelComponent.impl.label;
|
||||
try ipAddressLabel.updateText(ipAddress);
|
||||
}
|
||||
|
||||
fn discoverIpAddress() void {
|
||||
@ -86,13 +86,14 @@ fn copyIp() void {
|
||||
pub fn onOpen() Allocator.Error!void {
|
||||
var list = try VerticalList.init();
|
||||
try list.add(try Label.init(.{0, 16}, width, "Please send your IP to the host of the game and enter the host's IP below.", .center));
|
||||
// 255.255.255.255:?65536 (longest possible ip address)
|
||||
try list.add(try Label.init(.{0, 16}, width, " ", .center));
|
||||
// 255.255.255.255:?65536 (longest possible ip address)
|
||||
ipAddressLabel = try Label.init(.{0, 16}, width, " ", .center);
|
||||
try list.add(ipAddressLabel);
|
||||
try list.add(try Button.init(.{0, 16}, 100, "Copy IP", ©Ip));
|
||||
try list.add(try TextInput.init(.{0, 16}, width, 32, settings.lastUsedIPAddress));
|
||||
try list.add(try Button.init(.{0, 16}, 100, "Join", &join));
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
|
||||
thread = std.Thread.spawn(.{}, discoverIpAddressFromNewThread, .{}) catch |err| blk: {
|
||||
|
@ -29,7 +29,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
try list.add(try Button.init(.{0, 16}, 128, "Controls", gui.openWindowFunction("cubyz:controls")));
|
||||
try list.add(try Button.init(.{0, 16}, 128, "Change Name", gui.openWindowFunction("cubyz:change_name")));
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
var list = try VerticalList.init();
|
||||
// TODO
|
||||
components[0] = list.toComponent(.{padding, padding});
|
||||
window.contentSize = components[0].size + @splat(2, @as(f32, 2*padding));
|
||||
window.contentSize = components[0].size() + @splat(2, @as(f32, 2*padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user