mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 19:28:49 -04:00
Add the chat gui and set the default text color to white.
This commit is contained in:
parent
d5389aefb7
commit
77610c0064
Binary file not shown.
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
@ -30,13 +30,13 @@ fn fileToString(allocator: Allocator, path: []const u8) ![]u8 {
|
||||
}
|
||||
|
||||
pub const draw = struct {
|
||||
var color: i32 = 0;
|
||||
var color: u32 = 0;
|
||||
var clip: ?Vec4i = null;
|
||||
var translation: Vec2f = Vec2f{0, 0};
|
||||
var scale: f32 = 1;
|
||||
|
||||
pub fn setColor(newColor: u32) void {
|
||||
color = @bitCast(i32, newColor);
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
/// Returns the previous translation.
|
||||
@ -52,6 +52,7 @@ pub const draw = struct {
|
||||
|
||||
/// Returns the previous translation.
|
||||
pub fn setScale(newScale: f32) f32 {
|
||||
std.debug.assert(newScale >= 0);
|
||||
const oldScale = scale;
|
||||
scale *= newScale;
|
||||
return oldScale;
|
||||
@ -63,11 +64,12 @@ pub const draw = struct {
|
||||
|
||||
/// Returns the previous clip.
|
||||
pub fn setClip(clipRect: Vec2f) ?Vec4i {
|
||||
std.debug.assert(@reduce(.And, clipRect >= Vec2f{0, 0}));
|
||||
var newClip = Vec4i {
|
||||
@floatToInt(i32, translation[0]),
|
||||
main.Window.height - @floatToInt(i32, translation[1] + clipRect[1]*scale),
|
||||
@floatToInt(i32, clipRect[0]*scale),
|
||||
@floatToInt(i32, clipRect[1]*scale),
|
||||
std.math.lossyCast(i32, translation[0]),
|
||||
main.Window.height - std.math.lossyCast(i32, translation[1] + clipRect[1]*scale),
|
||||
std.math.lossyCast(i32, clipRect[0]*scale),
|
||||
std.math.lossyCast(i32, clipRect[1]*scale),
|
||||
};
|
||||
if(clip) |oldClip| {
|
||||
if (newClip[0] < oldClip[0]) {
|
||||
@ -84,6 +86,8 @@ pub const draw = struct {
|
||||
if (newClip[1] + newClip[3] > oldClip[1] + oldClip[3]) {
|
||||
newClip[3] -= (newClip[1] + newClip[3]) - (oldClip[1] + oldClip[3]);
|
||||
}
|
||||
newClip[2] = @max(newClip[2], 0);
|
||||
newClip[3] = @max(newClip[3], 0);
|
||||
} else {
|
||||
c.glEnable(c.GL_SCISSOR_TEST);
|
||||
}
|
||||
@ -151,7 +155,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(rectUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(rectUniforms.start, pos[0], pos[1]);
|
||||
c.glUniform2f(rectUniforms.size, dim[0], dim[1]);
|
||||
c.glUniform1i(rectUniforms.rectColor, color);
|
||||
c.glUniform1i(rectUniforms.rectColor, @bitCast(i32, color));
|
||||
|
||||
c.glBindVertexArray(rectVAO);
|
||||
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
|
||||
@ -204,7 +208,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(lineUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(lineUniforms.start, pos1[0], pos1[1]);
|
||||
c.glUniform2f(lineUniforms.direction, pos2[0] - pos1[0], pos2[1] - pos1[1]);
|
||||
c.glUniform1i(lineUniforms.lineColor, color);
|
||||
c.glUniform1i(lineUniforms.lineColor, @bitCast(i32, color));
|
||||
|
||||
c.glBindVertexArray(lineVAO);
|
||||
c.glDrawArrays(c.GL_LINE_STRIP, 0, 2);
|
||||
@ -250,7 +254,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(lineUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(lineUniforms.start, pos[0], pos[1]); // Move the coordinates, so they are in the center of a pixel.
|
||||
c.glUniform2f(lineUniforms.direction, dim[0] - 1, dim[1] - 1); // The height is a lot smaller because the inner edge of the rect is drawn.
|
||||
c.glUniform1i(lineUniforms.lineColor, color);
|
||||
c.glUniform1i(lineUniforms.lineColor, @bitCast(i32, color));
|
||||
|
||||
c.glBindVertexArray(lineVAO);
|
||||
c.glDrawArrays(c.GL_LINE_LOOP, 0, 5);
|
||||
@ -303,7 +307,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(circleUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(circleUniforms.center, center[0], center[1]); // Move the coordinates, so they are in the center of a pixel.
|
||||
c.glUniform1f(circleUniforms.radius, radius); // The height is a lot smaller because the inner edge of the rect is drawn.
|
||||
c.glUniform1i(circleUniforms.circleColor, color);
|
||||
c.glUniform1i(circleUniforms.circleColor, @bitCast(i32, color));
|
||||
|
||||
c.glBindVertexArray(circleVAO);
|
||||
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
|
||||
@ -341,7 +345,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(imageUniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(imageUniforms.start, pos[0], pos[1]);
|
||||
c.glUniform2f(imageUniforms.size, dim[0], dim[1]);
|
||||
c.glUniform1i(imageUniforms.color, color);
|
||||
c.glUniform1i(imageUniforms.color, @bitCast(i32, color));
|
||||
|
||||
c.glBindVertexArray(rectVAO);
|
||||
c.glDrawArrays(c.GL_TRIANGLE_STRIP, 0, 4);
|
||||
@ -359,7 +363,7 @@ pub const draw = struct {
|
||||
c.glUniform2f(uniforms.screen, @intToFloat(f32, Window.width), @intToFloat(f32, Window.height));
|
||||
c.glUniform2f(uniforms.start, pos[0], pos[1]);
|
||||
c.glUniform2f(uniforms.size, dim[0], dim[1]);
|
||||
c.glUniform1i(uniforms.color, color);
|
||||
c.glUniform1i(uniforms.color, @bitCast(i32, color));
|
||||
c.glUniform1f(uniforms.scale, scale);
|
||||
|
||||
c.glBindVertexArray(rectVAO);
|
||||
@ -382,7 +386,7 @@ pub const TextBuffer = struct {
|
||||
};
|
||||
|
||||
pub const FontEffect = packed struct(u28) {
|
||||
color: u24 = 0,
|
||||
color: u24 = 0xffffff,
|
||||
bold: bool = false,
|
||||
italic: bool = false,
|
||||
underline: bool = false,
|
||||
@ -741,6 +745,7 @@ pub const TextBuffer = struct {
|
||||
TextRendering.shader.bind();
|
||||
c.glUniform2f(TextRendering.uniforms.scene, @intToFloat(f32, main.Window.width), @intToFloat(f32, main.Window.height));
|
||||
c.glUniform1f(TextRendering.uniforms.ratio, draw.scale);
|
||||
c.glUniform1f(TextRendering.uniforms.alpha, @intToFloat(f32, draw.color >> 24) / 255.0);
|
||||
c.glActiveTexture(c.GL_TEXTURE0);
|
||||
c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]);
|
||||
c.glBindVertexArray(draw.rectVAO);
|
||||
@ -767,7 +772,7 @@ pub const TextBuffer = struct {
|
||||
y = 0;
|
||||
if(line.isUnderline) y += 15
|
||||
else y += 8;
|
||||
draw.setColor(line.color | @as(u32, 0xff000000));
|
||||
draw.setColor(line.color | (@as(u32, 0xff000000) & draw.color));
|
||||
for(lineWraps, 0..) |lineWrap, j| {
|
||||
const lineStart = @max(0, line.start);
|
||||
const lineEnd = @min(lineWrap, line.end);
|
||||
@ -856,6 +861,7 @@ const TextRendering = struct {
|
||||
const swap = glyphTexture[1];
|
||||
glyphTexture[1] = glyphTexture[0];
|
||||
glyphTexture[0] = swap;
|
||||
c.glActiveTexture(c.GL_TEXTURE0);
|
||||
c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]);
|
||||
c.glTexImage2D(c.GL_TEXTURE_2D, 0, c.GL_R8, newWidth, textureHeight, 0, c.GL_RED, c.GL_UNSIGNED_BYTE, null);
|
||||
c.glCopyImageSubData(
|
||||
@ -1349,8 +1355,7 @@ pub const TextureArray = struct {
|
||||
c.glTexSubImage3D(c.GL_TEXTURE_2D_ARRAY, lod, 0, 0, @intCast(c_int, i), curWidth, curHeight, 1, c.GL_RGBA, c.GL_UNSIGNED_BYTE, lodBuffer[lod].ptr);
|
||||
}
|
||||
}
|
||||
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_MAX_LOD, maxLOD);
|
||||
c.glTexParameteri(c.GL_TEXTURE_2D, c.GL_TEXTURE_BASE_LEVEL, 5);
|
||||
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MAX_LOD, maxLOD);
|
||||
//glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
|
||||
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MIN_FILTER, c.GL_NEAREST_MIPMAP_LINEAR);
|
||||
c.glTexParameteri(c.GL_TEXTURE_2D_ARRAY, c.GL_TEXTURE_MAG_FILTER, c.GL_NEAREST);
|
||||
|
@ -63,6 +63,8 @@ isHud: bool = false,
|
||||
|
||||
/// Called every frame.
|
||||
renderFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
|
||||
/// Called every frame before rendering.
|
||||
updateFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
|
||||
/// Called every frame for the currently selected window.
|
||||
updateSelectedFn: *const fn()Allocator.Error!void = &defaultErrorFunction,
|
||||
/// Called every frame for the currently hovered window.
|
||||
@ -310,6 +312,10 @@ fn positionRelativeToConnectedWindow(self: *GuiWindow, other: *GuiWindow, i: usi
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(self: *GuiWindow) !void {
|
||||
try self.updateFn();
|
||||
}
|
||||
|
||||
pub fn updateSelected(self: *GuiWindow, mousePosition: Vec2f) !void {
|
||||
try self.updateSelectedFn();
|
||||
const windowSize = main.Window.getWindowSize()/@splat(2, gui.scale);
|
||||
@ -478,7 +484,7 @@ pub fn render(self: *const GuiWindow, mousePosition: Vec2f) !void {
|
||||
draw.restoreTranslation(oldTranslation);
|
||||
draw.restoreScale(oldScale);
|
||||
if(self.showTitleBar) {
|
||||
var text = try graphics.TextBuffer.init(gui.allocator, self.title, .{}, false, .center);
|
||||
var text = try graphics.TextBuffer.init(gui.allocator, self.title, .{.color=0}, false, .center);
|
||||
defer text.deinit();
|
||||
const titleDimension = try text.calculateLineBreaks(16*self.scale, self.size[0]);
|
||||
try text.render(self.pos[0] + self.size[0]/2 - titleDimension[0]/2, self.pos[1], 16*self.scale);
|
||||
|
@ -18,6 +18,7 @@ const fontSize: f32 = 16;
|
||||
pos: Vec2f,
|
||||
size: Vec2f,
|
||||
text: TextBuffer,
|
||||
alpha: f32 = 1,
|
||||
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, text: []const u8, alignment: TextBuffer.Alignment) Allocator.Error!*Label {
|
||||
const self = try gui.allocator.create(Label);
|
||||
@ -49,5 +50,6 @@ pub fn updateText(self: *Label, newText: []const u8) !void {
|
||||
}
|
||||
|
||||
pub fn render(self: *Label, _: Vec2f) !void {
|
||||
draw.setColor(@floatToInt(u32, self.alpha*255) << 24);
|
||||
try self.text.render(self.pos[0], self.pos[1], fontSize);
|
||||
}
|
79
src/gui/components/MutexComponent.zig
Normal file
79
src/gui/components/MutexComponent.zig
Normal file
@ -0,0 +1,79 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const main = @import("root");
|
||||
const graphics = main.graphics;
|
||||
const draw = graphics.draw;
|
||||
const TextBuffer = graphics.TextBuffer;
|
||||
const vec = main.vec;
|
||||
const Vec2f = vec.Vec2f;
|
||||
|
||||
const gui = @import("../gui.zig");
|
||||
const GuiComponent = gui.GuiComponent;
|
||||
const ScrollBar = GuiComponent.ScrollBar;
|
||||
|
||||
const MutexComponent = @This();
|
||||
|
||||
const scrollBarWidth = 5;
|
||||
const border: f32 = 3;
|
||||
|
||||
pos: Vec2f = undefined,
|
||||
size: Vec2f = undefined,
|
||||
child: GuiComponent = undefined,
|
||||
mutex: std.Thread.Mutex = .{},
|
||||
|
||||
pub fn updateInner(self: *MutexComponent, _other: anytype) Allocator.Error!void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // self.mutex must be locked before calling!
|
||||
var other: GuiComponent = undefined;
|
||||
if(@TypeOf(_other) == GuiComponent) {
|
||||
other = _other;
|
||||
} else {
|
||||
other = _other.toComponent();
|
||||
}
|
||||
self.child = other;
|
||||
self.pos = other.pos();
|
||||
self.size = other.size();
|
||||
}
|
||||
|
||||
pub fn deinit(self: *MutexComponent) void {
|
||||
std.debug.assert(!self.mutex.tryLock()); // self.mutex must be locked before calling!
|
||||
self.child.deinit();
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *MutexComponent) GuiComponent {
|
||||
return GuiComponent {
|
||||
.mutexComponent = self
|
||||
};
|
||||
}
|
||||
|
||||
pub fn updateSelected(self: *MutexComponent) void {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
self.child.updateSelected();
|
||||
}
|
||||
|
||||
pub fn updateHovered(self: *MutexComponent, mousePosition: Vec2f) void {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
self.child.updateHovered(mousePosition);
|
||||
}
|
||||
|
||||
pub fn render(self: *MutexComponent, mousePosition: Vec2f) anyerror!void { // TODO: Remove anyerror once error union inference works in recursive loops.
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
try self.child.render(mousePosition);
|
||||
self.pos = self.child.pos();
|
||||
self.size = self.child.size();
|
||||
}
|
||||
|
||||
pub fn mainButtonPressed(self: *MutexComponent, mousePosition: Vec2f) void {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
self.child.mainButtonPressed(mousePosition);
|
||||
}
|
||||
|
||||
pub fn mainButtonReleased(self: *MutexComponent, mousePosition: Vec2f) void {
|
||||
self.mutex.lock();
|
||||
defer self.mutex.unlock();
|
||||
self.child.mainButtonReleased(mousePosition);
|
||||
}
|
@ -33,6 +33,7 @@ maxWidth: f32,
|
||||
maxHeight: f32,
|
||||
textSize: Vec2f = undefined,
|
||||
scrollBar: *ScrollBar,
|
||||
onNewline: ?*const fn() void,
|
||||
|
||||
pub fn __init() !void {
|
||||
texture = try Texture.initFromFile("assets/cubyz/ui/text_input.png");
|
||||
@ -42,7 +43,7 @@ pub fn __deinit() void {
|
||||
texture.deinit();
|
||||
}
|
||||
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocator.Error!*TextInput {
|
||||
pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8, onNewline: ?*const fn() void) Allocator.Error!*TextInput {
|
||||
const scrollBar = try ScrollBar.init(undefined, scrollBarWidth, maxHeight - 2*border, 0);
|
||||
const self = try gui.allocator.create(TextInput);
|
||||
self.* = TextInput {
|
||||
@ -53,6 +54,7 @@ pub fn init(pos: Vec2f, maxWidth: f32, maxHeight: f32, text: []const u8) Allocat
|
||||
.maxWidth = maxWidth,
|
||||
.maxHeight = maxHeight,
|
||||
.scrollBar = scrollBar,
|
||||
.onNewline = onNewline,
|
||||
};
|
||||
try self.currentString.appendSlice(text);
|
||||
self.textSize = try self.textBuffer.calculateLineBreaks(fontSize, maxWidth - 2*border - scrollBarWidth);
|
||||
@ -66,6 +68,15 @@ pub fn deinit(self: *const TextInput) void {
|
||||
gui.allocator.destroy(self);
|
||||
}
|
||||
|
||||
pub fn clear(self: *TextInput) !void {
|
||||
if(self.cursor != null) {
|
||||
self.cursor = 0;
|
||||
self.selectionStart = null;
|
||||
}
|
||||
self.currentString.clearRetainingCapacity();
|
||||
try self.reloadText();
|
||||
}
|
||||
|
||||
pub fn toComponent(self: *TextInput) GuiComponent {
|
||||
return GuiComponent {
|
||||
.textInput = self
|
||||
@ -420,7 +431,11 @@ pub fn cut(self: *TextInput, mods: main.Key.Modifiers) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn newline(self: *TextInput, _: main.Key.Modifiers) void {
|
||||
pub fn newline(self: *TextInput, mods: main.Key.Modifiers) void {
|
||||
if(!mods.shift) if(self.onNewline) |onNewline| {
|
||||
onNewline();
|
||||
return;
|
||||
};
|
||||
self.inputCharacter('\n') catch |err| {
|
||||
std.log.err("Error while entering text: {s}", .{@errorName(err)});
|
||||
};
|
||||
|
@ -17,7 +17,7 @@ const TextInput = @import("components/TextInput.zig");
|
||||
pub const GuiComponent = @import("gui_component.zig").GuiComponent;
|
||||
pub const GuiWindow = @import("GuiWindow.zig");
|
||||
|
||||
const windowlist = @import("windows/_windowlist.zig");
|
||||
pub const windowlist = @import("windows/_windowlist.zig");
|
||||
|
||||
var windowList: std.ArrayList(*GuiWindow) = undefined;
|
||||
var hudWindows: std.ArrayList(*GuiWindow) = undefined;
|
||||
@ -402,6 +402,9 @@ pub fn updateAndRenderGui() !void {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(openWindows.items) |window| {
|
||||
try window.update();
|
||||
}
|
||||
for(openWindows.items) |window| {
|
||||
const oldScale = draw.setScale(scale);
|
||||
defer draw.restoreScale(oldScale);
|
||||
|
@ -10,6 +10,7 @@ pub const GuiComponent = union(enum) {
|
||||
pub const CheckBox = @import("components/CheckBox.zig");
|
||||
pub const HorizontalList = @import("components/HorizontalList.zig");
|
||||
pub const Label = @import("components/Label.zig");
|
||||
pub const MutexComponent = @import("components/MutexComponent.zig");
|
||||
pub const Slider = @import("components/Slider.zig");
|
||||
pub const ScrollBar = @import("components/ScrollBar.zig");
|
||||
pub const TextInput = @import("components/TextInput.zig");
|
||||
@ -20,6 +21,7 @@ pub const GuiComponent = union(enum) {
|
||||
checkBox: *CheckBox,
|
||||
horizontalList: *HorizontalList,
|
||||
label: *Label,
|
||||
mutexComponent: *MutexComponent,
|
||||
scrollBar: *ScrollBar,
|
||||
slider: *Slider,
|
||||
textInput: *TextInput,
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
pub const change_name = @import("change_name.zig");
|
||||
pub const chat = @import("chat.zig");
|
||||
pub const controls = @import("controls.zig");
|
||||
pub const crosshair = @import("crosshair.zig");
|
||||
pub const graphics = @import("graphics.zig");
|
||||
|
@ -49,13 +49,13 @@ pub fn onOpen() Allocator.Error!void {
|
||||
if(settings.playerName.len == 0) {
|
||||
try list.add(try Label.init(.{0, 0}, width, "Please enter your name!", .center));
|
||||
} else {
|
||||
try list.add(try Label.init(.{0, 0}, width, "#ff0000Warning: #000000You loose access to your inventory data when changing the name!", .center));
|
||||
try list.add(try Label.init(.{0, 0}, width, "#ff0000Warning: #ffffffYou loose access to your inventory data when changing the name!", .center));
|
||||
}
|
||||
try list.add(try Label.init(.{0, 0}, width, "Cubyz supports formatting your username using a markdown-like syntax:", .center));
|
||||
try list.add(try Label.init(.{0, 0}, width, "\\**italic*\\* \\*\\***bold**\\*\\* \\__underlined_\\_ \\_\\___strike-through__\\_\\_", .center));
|
||||
try list.add(try Label.init(.{0, 0}, width, "Even colors are possible, using the hexadecimal color code:", .center));
|
||||
try list.add(try Label.init(.{0, 0}, width, "\\##ff0000ff#00000000#00000000#ff0000red#000000 \\##ff0000ff#00770077#00000000#ff7700orange#000000 \\##00000000#00ff00ff#00000000#00ff00green#000000 \\##00000000#00000000#0000ffff#0000ffblue", .center));
|
||||
textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper");
|
||||
try list.add(try Label.init(.{0, 0}, width, "\\##ff0000ff#ffffff00#ffffff00#ff0000red#ffffff \\##ff0000ff#00770077#ffffff00#ff7700orange#ffffff \\##ffffff00#00ff00ff#ffffff00#00ff00green#ffffff \\##ffffff00#ffffff00#0000ffff#0000ffblue", .center));
|
||||
textComponent = try TextInput.init(.{0, 0}, width, 32, "quanturmdoelvloper", &apply);
|
||||
try list.add(textComponent);
|
||||
try list.add(try Button.init(.{0, 0}, 100, "Apply", &apply));
|
||||
list.finish(.center);
|
||||
|
132
src/gui/windows/chat.zig
Normal file
132
src/gui/windows/chat.zig
Normal file
@ -0,0 +1,132 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const main = @import("root");
|
||||
const Vec2f = main.vec.Vec2f;
|
||||
|
||||
const gui = @import("../gui.zig");
|
||||
const GuiComponent = gui.GuiComponent;
|
||||
const GuiWindow = gui.GuiWindow;
|
||||
const Button = @import("../components/Button.zig");
|
||||
const Label = GuiComponent.Label;
|
||||
const MutexComponent = GuiComponent.MutexComponent;
|
||||
const TextInput = GuiComponent.TextInput;
|
||||
const VerticalList = @import("../components/VerticalList.zig");
|
||||
|
||||
var components: [1]GuiComponent = undefined;
|
||||
pub var window: GuiWindow = GuiWindow {
|
||||
.contentSize = Vec2f{128, 256},
|
||||
.id = "cubyz:chat",
|
||||
.title = "Chat",
|
||||
.onOpenFn = &onOpen,
|
||||
.onCloseFn = &onClose,
|
||||
.updateFn = &update,
|
||||
.renderFn = &render,
|
||||
.components = &components,
|
||||
.showTitleBar = false,
|
||||
.hasBackground = false,
|
||||
.isHud = true,
|
||||
};
|
||||
|
||||
const padding: f32 = 8;
|
||||
const messageTimeout: i32 = 10000;
|
||||
const messageFade: i32 = 1000;
|
||||
|
||||
var mutexComponent: MutexComponent = .{};
|
||||
var history: std.ArrayList(*Label) = undefined;
|
||||
var expirationTime: std.ArrayList(i32) = undefined;
|
||||
var historyStart: u32 = 0;
|
||||
var fadeOutEnd: u32 = 0;
|
||||
var input: *TextInput = undefined;
|
||||
var hideInput: bool = true;
|
||||
|
||||
fn refresh(deleteOld: bool) Allocator.Error!void {
|
||||
std.debug.assert(!mutexComponent.mutex.tryLock()); // mutex must be locked!
|
||||
if(deleteOld) {
|
||||
components[0].mutexComponent.child.verticalList.children.clearRetainingCapacity();
|
||||
components[0].deinit();
|
||||
}
|
||||
var list = try VerticalList.init(.{padding, 16 + padding}, 300, 0);
|
||||
for(history.items[if(hideInput) historyStart else 0 ..]) |msg| {
|
||||
msg.pos = .{0, 0};
|
||||
try list.add(msg);
|
||||
}
|
||||
if(!hideInput) {
|
||||
input.pos = .{0, 0};
|
||||
try list.add(input);
|
||||
}
|
||||
list.finish(.center);
|
||||
list.scrollBar.currentState = 1;
|
||||
try mutexComponent.updateInner(list);
|
||||
components[0] = mutexComponent.toComponent();
|
||||
window.contentSize = components[0].pos() + components[0].size() + @splat(2, @as(f32, padding));
|
||||
gui.updateWindowPositions();
|
||||
}
|
||||
|
||||
pub fn onOpen() Allocator.Error!void {
|
||||
history = std.ArrayList(*Label).init(main.globalAllocator);
|
||||
expirationTime = std.ArrayList(i32).init(main.globalAllocator);
|
||||
historyStart = 0;
|
||||
input = try TextInput.init(.{0, 0}, 256, 32, "", &sendMessage);
|
||||
mutexComponent.mutex.lock();
|
||||
defer mutexComponent.mutex.unlock();
|
||||
try refresh(false);
|
||||
}
|
||||
|
||||
pub fn onClose() void {
|
||||
mutexComponent.mutex.lock();
|
||||
defer mutexComponent.mutex.unlock();
|
||||
for(history.items) |label| {
|
||||
label.deinit();
|
||||
}
|
||||
history.deinit();
|
||||
expirationTime.deinit();
|
||||
input.deinit();
|
||||
components[0].mutexComponent.child.verticalList.children.clearRetainingCapacity();
|
||||
components[0].deinit();
|
||||
}
|
||||
|
||||
pub fn update() Allocator.Error!void {
|
||||
mutexComponent.mutex.lock();
|
||||
defer mutexComponent.mutex.unlock();
|
||||
while(fadeOutEnd < history.items.len and @truncate(i32, std.time.milliTimestamp()) -% expirationTime.items[fadeOutEnd] >= 0) {
|
||||
fadeOutEnd += 1;
|
||||
}
|
||||
for(expirationTime.items[historyStart..fadeOutEnd], history.items[historyStart..fadeOutEnd]) |time, label| {
|
||||
if(@truncate(i32, std.time.milliTimestamp()) -% time >= messageFade) {
|
||||
historyStart += 1;
|
||||
hideInput = main.Window.grabbed;
|
||||
try refresh(true);
|
||||
} else {
|
||||
label.alpha = 1.0 - @intToFloat(f32, @truncate(i32, std.time.milliTimestamp()) -% time)/@intToFloat(f32, messageFade);
|
||||
}
|
||||
}
|
||||
if(hideInput != main.Window.grabbed) {
|
||||
hideInput = main.Window.grabbed;
|
||||
try refresh(true);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render() Allocator.Error!void {
|
||||
if(!hideInput) {
|
||||
main.graphics.draw.setColor(0x80000000);
|
||||
main.graphics.draw.rect(.{0, 0}, window.contentSize);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addMessage(message: []const u8) Allocator.Error!void {
|
||||
mutexComponent.mutex.lock();
|
||||
defer mutexComponent.mutex.unlock();
|
||||
try history.append(try Label.init(.{0, 0}, 256, message, .left));
|
||||
try expirationTime.append(@truncate(i32, std.time.milliTimestamp()) +% messageTimeout);
|
||||
try refresh(true);
|
||||
}
|
||||
|
||||
pub fn sendMessage() void {
|
||||
main.network.Protocols.chat.send(main.game.world.?.conn, input.currentString.items) catch |err| {
|
||||
std.log.err("Got error while trying to send chat message: {s}", .{@errorName(err)});
|
||||
};
|
||||
input.clear() catch |err| {
|
||||
std.log.err("Got error while trying to send chat message: {s}", .{@errorName(err)});
|
||||
};
|
||||
}
|
@ -21,6 +21,7 @@ pub var window = GuiWindow {
|
||||
.title = "Multiplayer",
|
||||
.onOpenFn = &onOpen,
|
||||
.onCloseFn = &onClose,
|
||||
.updateFn = &update,
|
||||
.components = &components,
|
||||
};
|
||||
|
||||
@ -37,7 +38,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});
|
||||
gotIpAddress.store(true, .Monotonic);
|
||||
gotIpAddress.store(true, .Release);
|
||||
}
|
||||
|
||||
fn discoverIpAddress() void {
|
||||
@ -94,7 +95,7 @@ pub fn onOpen() Allocator.Error!void {
|
||||
ipAddressLabel = try Label.init(.{0, 0}, width, " ", .center);
|
||||
try list.add(ipAddressLabel);
|
||||
try list.add(try Button.init(.{0, 0}, 100, "Copy IP", ©Ip));
|
||||
try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress));
|
||||
try list.add(try TextInput.init(.{0, 0}, width, 32, settings.lastUsedIPAddress, &join));
|
||||
try list.add(try Button.init(.{0, 0}, 100, "Join", &join));
|
||||
list.finish(.center);
|
||||
components[0] = list.toComponent();
|
||||
@ -127,8 +128,8 @@ pub fn onClose() void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render() void {
|
||||
if(gotIpAddress.load(.Monotonic)) {
|
||||
pub fn update() Allocator.Error!void {
|
||||
if(gotIpAddress.load(.Acquire)) {
|
||||
gotIpAddress.store(false, .Monotonic);
|
||||
try ipAddressLabel.updateText(ipAddress);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
const std = @import("std");
|
||||
|
||||
const gui = @import("gui");
|
||||
pub const gui = @import("gui");
|
||||
|
||||
pub const assets = @import("assets.zig");
|
||||
pub const blocks = @import("blocks.zig");
|
||||
|
@ -1201,6 +1201,38 @@ pub const Protocols: struct {
|
||||
// addHeaderAndSendUnimportant(user, TIME_AND_BIOME, data.toString().getBytes(StandardCharsets.UTF_8));
|
||||
// }
|
||||
},
|
||||
chat: type = struct {
|
||||
const id: u8 = 10;
|
||||
fn receive(conn: *Connection, data: []const u8) !void {
|
||||
_ = conn;
|
||||
// TODO:
|
||||
// if(conn instanceof User) {
|
||||
// User user = (User)conn;
|
||||
// if(msg.startsWith("/")) {
|
||||
// CommandExecutor.execute(msg, user);
|
||||
// } else {
|
||||
// msg = "["+user.name+"#ffffff] "+msg;
|
||||
// sendToClients(msg);
|
||||
// }
|
||||
// } else {
|
||||
try main.gui.windowlist.chat.addMessage(data);
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn send(conn: *Connection, data: []const u8) !void {
|
||||
try conn.sendImportant(id, data);
|
||||
}
|
||||
// TODO
|
||||
// public void sendToClients(String msg) {
|
||||
// Logger.log("chat", msg, "\033[0;32m");
|
||||
// synchronized(this) {
|
||||
// for(User user : Server.users) {
|
||||
// send(user, msg);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
},
|
||||
} = .{};
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user