diff --git a/build.zig b/build.zig index f9989f1..e71c047 100644 --- a/build.zig +++ b/build.zig @@ -39,10 +39,17 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .root_source_file = b.path("src/env.zig"), }) }); + // What Enter does in the message box. + const composer_tests = b.addTest(.{ .root_module = b.createModule(.{ + .target = target, + .optimize = optimize, + .root_source_file = b.path("src/composer.zig"), + }) }); const test_step = b.step("test", "Run the chat's tests"); test_step.dependOn(&b.addRunArtifact(auth_tests).step); test_step.dependOn(&b.addRunArtifact(tests).step); test_step.dependOn(&b.addRunArtifact(transcript_tests).step); test_step.dependOn(&b.addRunArtifact(permission_tests).step); test_step.dependOn(&b.addRunArtifact(env_tests).step); + test_step.dependOn(&b.addRunArtifact(composer_tests).step); } diff --git a/src/View.zig b/src/View.zig index 49caf9f..66d0e6a 100644 --- a/src/View.zig +++ b/src/View.zig @@ -18,6 +18,7 @@ const api = @import("api.zig"); const Chat = @import("Chat.zig"); const Commands = @import("Commands.zig"); const Transcript = @import("Transcript.zig"); +const composer = @import("composer.zig"); const entypo = dvui.entypo; @@ -815,14 +816,14 @@ fn drawInput(c: *Chat) void { .gravity_y = 1, .corners = .all(14), .padding = .{ .x = 10, .y = 6, .w = 10, .h = 6 }, - // A line tall to start, growing with what is typed up to six lines, then scrolling. + // A line tall to start, growing with what is typed up to eight lines, then scrolling. .min_size_content = .{ .w = 40, .h = theme.font_body.lineHeight() }, - .max_size_content = .{ .w = std.math.floatMax(f32), .h = theme.font_body.lineHeight() * 6 }, + .max_size_content = .{ .w = std.math.floatMax(f32), .h = theme.font_body.lineHeight() * composer.max_lines }, }); - // Enter sends; Shift+Enter is a new line, as the entry would make it. + // Enter sends on a desktop; on a phone, and with Shift, it is a new line, as the entry would make it. for (dvui.events()) |*e| { if (!te.matchEvent(e)) continue; - if (e.evt == .key and e.evt.key.action == .down and e.evt.key.code == .enter and !e.evt.key.mod.shift()) { + if (e.evt == .key and e.evt.key.action == .down and e.evt.key.code == .enter and composer.enter(e.evt.key.mod.shift(), composer.isMobile()) == .send) { e.handle(@src(), te.data()); send_now = true; } diff --git a/src/composer.zig b/src/composer.zig new file mode 100644 index 0000000..22d5aec --- /dev/null +++ b/src/composer.zig @@ -0,0 +1,34 @@ +//! What the message box decides, std-only so it tests without a window: what Enter does and how +//! tall the box grows. +const std = @import("std"); +const builtin = @import("builtin"); + +/// Lines the message box grows to; past this it scrolls. +pub const max_lines: f32 = 8; + +pub const Enter = enum { send, newline }; + +/// Enter sends on a desktop and is a new line on a phone, where the Send button sends; Shift+Enter +/// is a new line anywhere. +pub fn enter(shift: bool, mobile: bool) Enter { + return if (shift or mobile) .newline else .send; +} + +/// Whether this build runs on a phone or tablet. The SDK does not hand plugins the app's form +/// factor yet, so this is the target's own answer: a phone's browser still reads as a desktop. +pub fn isMobile() bool { + return builtin.os.tag == .ios or builtin.abi.isAndroid(); +} + +test "Enter sends on a desktop and breaks the line on a phone or with Shift" { + try std.testing.expectEqual(Enter.send, enter(false, false)); + try std.testing.expectEqual(Enter.newline, enter(true, false)); + try std.testing.expectEqual(Enter.newline, enter(false, true)); + try std.testing.expectEqual(Enter.newline, enter(true, true)); +} + +test "this desktop build is not mobile" { + if (builtin.os.tag == .macos or builtin.os.tag == .linux or builtin.os.tag == .windows) { + if (!builtin.abi.isAndroid()) try std.testing.expect(!isMobile()); + } +}