mirror of https://git.sr.ht/~kikoodx/kble
Compare commits
4 Commits
25335e94a6
...
a3a4483a44
Author | SHA1 | Date |
---|---|---|
KikooDX | a3a4483a44 | |
KikooDX | 4fb7d33645 | |
KikooDX | 9a0bbb8e95 | |
KikooDX | eec14bc99c |
18
README.md
18
README.md
|
@ -20,8 +20,8 @@ https://builds.sr.ht/~kikoodx/kble
|
|||
|
||||
## Manual compilation
|
||||
Build requirements: [Zig](https://ziglang.org/download/) master branch,
|
||||
[raylib](https://www.raylib.com).
|
||||
For Arch users, `zig-dev-bin` and `zig-git` are available in the AUR.
|
||||
[raylib](https://www.raylib.com). For Arch users, I made
|
||||
[`kble-git`](https://aur.archlinux.org/packages/kble-git/) available in the AUR.
|
||||
```sh
|
||||
$ # Clone this repository
|
||||
$ git clone https://git.sr.ht/~kikoodx/kble && cd kble
|
||||
|
@ -79,14 +79,16 @@ Modes:
|
|||
* `<return>`: normal mode, default
|
||||
* `i`: free selection mode
|
||||
* `v`: rectangle selection mode
|
||||
* `I`: free unselection mode
|
||||
* `V`: rectangle unselection mode
|
||||
* `c`: camera mode [not implemented]
|
||||
|
||||
# Mouse control
|
||||
Right click and left click are the same, except right click reset
|
||||
selection before taking effect.
|
||||
* Click: free selection.
|
||||
* Shift + Click: rectangle selection.
|
||||
* Click while selecting: end selection.
|
||||
# Default mouse control
|
||||
* Right click: free selection.
|
||||
* Shift + left click: rectangle selection.
|
||||
* Right click: libre unselection.
|
||||
* Shift + right click: rectangle unselection.
|
||||
* Left or right click while un·selecting: end selection.
|
||||
|
||||
# License
|
||||
Copyright (c) 2021 KikooDX
|
||||
|
|
|
@ -143,6 +143,14 @@ pub const ActionsDef = .{
|
|||
.category = ActionCat.mode,
|
||||
.next_mode = Mode.rectangle,
|
||||
},
|
||||
.mode_unselect = Action{
|
||||
.category = ActionCat.mode,
|
||||
.next_mode = Mode.unselect,
|
||||
},
|
||||
.mode_unrectangle = Action{
|
||||
.category = ActionCat.mode,
|
||||
.next_mode = Mode.unrectangle,
|
||||
},
|
||||
.mode_camera = Action{
|
||||
.category = ActionCat.mode,
|
||||
.next_mode = Mode.camera,
|
||||
|
|
11
src/conf.zig
11
src/conf.zig
|
@ -21,6 +21,15 @@ fn col(r: u8, g: u8, b: u8) ray.Color {
|
|||
pub const mouse_enabled: bool = true;
|
||||
pub const mouse_left_btn: c_int = 0;
|
||||
pub const mouse_right_btn: c_int = 1;
|
||||
// Set to true for better graphical tablet experience.
|
||||
// * false: click to enter, draw, click to exit.
|
||||
// * true: click to enter, draw, release to exit.
|
||||
pub const mouse_graphic_tablet: bool = false;
|
||||
|
||||
pub const default_grid_size = .{
|
||||
.width = 16,
|
||||
.height = 16,
|
||||
};
|
||||
|
||||
pub const theme = .{
|
||||
.background = ray.BLACK,
|
||||
|
@ -28,6 +37,8 @@ pub const theme = .{
|
|||
.normal = ray.GRAY,
|
||||
.select = ray.BLUE,
|
||||
.select_rect = ray.SKYBLUE,
|
||||
.unselect = ray.RED,
|
||||
.unrectangle = ray.PINK,
|
||||
.camera = ray.PURPLE,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,16 +19,21 @@ pub fn cursor(scale: scaling.scale_type, offset: Vec2, pos: Vec2, mode: Mode) vo
|
|||
.normal => conf.theme.mode.normal,
|
||||
.select => conf.theme.mode.select,
|
||||
.rectangle => conf.theme.mode.select_rect,
|
||||
.unselect => conf.theme.mode.unselect,
|
||||
.unrectangle => conf.theme.mode.unrectangle,
|
||||
.camera => conf.theme.mode.camera,
|
||||
};
|
||||
ray.DrawRectangleLines(x, y, scale, scale, color);
|
||||
}
|
||||
|
||||
/// Draw rectangle selection preview.
|
||||
pub fn rectangle_selection(scale: scaling.scale_type, offset: Vec2, rect: Rect) void {
|
||||
pub fn rectangle_selection(scale: scaling.scale_type, offset: Vec2, rect: Rect, sel: bool) void {
|
||||
const x = (rect.left_x - offset.x) * scale;
|
||||
const w = (rect.right_x - rect.left_x + 1) * scale;
|
||||
const y = (rect.top_y - offset.y) * scale;
|
||||
const h = (rect.bottom_y - rect.top_y + 1) * scale;
|
||||
ray.DrawRectangleLines(x, y, w, h, conf.theme.mode.select_rect);
|
||||
ray.DrawRectangleLines(x, y, w, h, if (sel)
|
||||
conf.theme.mode.select_rect
|
||||
else
|
||||
conf.theme.mode.unrectangle);
|
||||
}
|
||||
|
|
76
src/main.zig
76
src/main.zig
|
@ -24,6 +24,7 @@ const ActionCat = actions.ActionCat;
|
|||
const Mode = @import("modes.zig").Mode;
|
||||
|
||||
const char_range = 255;
|
||||
const input_buffer_len = 255;
|
||||
|
||||
pub fn main() void {
|
||||
const level_path: [*:0]const u8 = if (std.os.argv.len > 1)
|
||||
|
@ -48,7 +49,8 @@ pub fn main() void {
|
|||
|
||||
// Try to load level, is doesn't exist create it.
|
||||
var level: Level = Level.init_read(allocator, level_path) catch create: {
|
||||
break :create Level.init(allocator, 16, 16) catch unreachable;
|
||||
break :create Level.init(allocator, conf.default_grid_size.width, conf.default_grid_size.height) catch
|
||||
unreachable;
|
||||
};
|
||||
defer level.deinit(allocator);
|
||||
|
||||
|
@ -109,6 +111,8 @@ pub fn main() void {
|
|||
default_bindings['\n'] = &ActionsDef.mode_normal;
|
||||
default_bindings['i'] = &ActionsDef.mode_select;
|
||||
default_bindings['v'] = &ActionsDef.mode_rectangle;
|
||||
default_bindings['I'] = &ActionsDef.mode_unselect;
|
||||
default_bindings['V'] = &ActionsDef.mode_unrectangle;
|
||||
default_bindings['c'] = &ActionsDef.mode_camera;
|
||||
// Map user bindings.
|
||||
comptime {
|
||||
|
@ -120,7 +124,6 @@ pub fn main() void {
|
|||
}
|
||||
|
||||
// Create input buffer.
|
||||
const input_buffer_len = 255;
|
||||
var input_buffer: [input_buffer_len]u32 = undefined;
|
||||
comptime {
|
||||
var i: u8 = 0;
|
||||
|
@ -132,28 +135,18 @@ pub fn main() void {
|
|||
|
||||
while (!ray.WindowShouldClose()) {
|
||||
{
|
||||
// TODO: apply DRY
|
||||
// Get keyboard input.
|
||||
var key = ray.GetCharPressed();
|
||||
// Check if more characters have been pressed.
|
||||
while (key != 0) {
|
||||
// Add key to buffer.
|
||||
input_buffer[input_cursor] = @intCast(u32, key);
|
||||
input_cursor += 1;
|
||||
// Avoid writing out of memory.
|
||||
if (input_cursor >= input_buffer_len)
|
||||
input_cursor = input_buffer_len - 1;
|
||||
|
||||
add_key_to_buffer(&input_buffer, &input_cursor, key);
|
||||
key = ray.GetCharPressed();
|
||||
}
|
||||
// Check for special keys, not detected by GetCharPressed.
|
||||
if (ray.IsKeyDown(ray.KEY_ENTER)) {
|
||||
input_buffer[input_cursor] = '\n';
|
||||
input_cursor += 1;
|
||||
// Avoid writing out of memory.
|
||||
if (input_cursor >= input_buffer_len)
|
||||
input_cursor = input_buffer_len - 1;
|
||||
}
|
||||
if (ray.IsKeyPressed(ray.KEY_ENTER))
|
||||
add_key_to_buffer(&input_buffer, &input_cursor, '\n');
|
||||
if (ray.IsKeyPressed(ray.KEY_TAB))
|
||||
add_key_to_buffer(&input_buffer, &input_cursor, '\t');
|
||||
}
|
||||
|
||||
// Process buffer content.
|
||||
|
@ -167,7 +160,7 @@ pub fn main() void {
|
|||
|
||||
// TODO: move everything from this to a function (for config and macros support).
|
||||
const action: actions.Action = bindings[key].*;
|
||||
const apply_selection: bool = (mode == Mode.select);
|
||||
const apply_selection: bool = (mode == .select);
|
||||
|
||||
switch (action.category) {
|
||||
.none => std.log.info("No action bound to {}.", .{key}),
|
||||
|
@ -192,13 +185,13 @@ pub fn main() void {
|
|||
},
|
||||
.mode => {
|
||||
// Rectangle selection!
|
||||
if (mode == Mode.rectangle) {
|
||||
if (mode == .rectangle or mode == .unrectangle) {
|
||||
const selection = Rect.init_from_vec2(cursor, cursor_before);
|
||||
level.select_rect(selection, true);
|
||||
level.select_rect(selection, mode == .rectangle);
|
||||
}
|
||||
cursor_before = cursor; // Save position before selection.
|
||||
mode = action.next_mode;
|
||||
if (mode == Mode.select) // Select first tile.
|
||||
if (mode == .select) // Select first tile.
|
||||
level.select_cell(cursor, true);
|
||||
},
|
||||
}
|
||||
|
@ -217,17 +210,21 @@ pub fn main() void {
|
|||
}
|
||||
|
||||
const left_click: bool = ray.IsMouseButtonPressed(conf.mouse_left_btn);
|
||||
const left_release: bool = ray.IsMouseButtonReleased(conf.mouse_left_btn);
|
||||
const right_click: bool = ray.IsMouseButtonPressed(conf.mouse_right_btn);
|
||||
const right_release: bool = ray.IsMouseButtonReleased(conf.mouse_right_btn);
|
||||
const click: bool = left_click or right_click;
|
||||
const release: bool = left_release or right_release;
|
||||
const end_sel_event = if (conf.mouse_graphic_tablet) release else click;
|
||||
|
||||
// If click, get out of current mode and apply changes if necessary.
|
||||
if (click and mouse.mode != Mouse.MouseMode.idle) {
|
||||
defer mouse.mode = Mouse.MouseMode.wait;
|
||||
// When end selection event, get out of current mode and apply changes if necessary.
|
||||
if (end_sel_event and mouse.mode != Mouse.MouseMode.idle) {
|
||||
defer mouse.mode = .wait;
|
||||
|
||||
// Select area.
|
||||
if (mouse.mode == Mouse.MouseMode.rect_sel) {
|
||||
if (mouse.mode == .rect_sel or mouse.mode == .unrect_sel) {
|
||||
const selection = Rect.init_from_vec2(mouse.start_pos, mouse.pos);
|
||||
level.select_rect(selection, true);
|
||||
level.select_rect(selection, mouse.mode == .rect_sel);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -240,22 +237,23 @@ pub fn main() void {
|
|||
ray.IsKeyDown(ray.KEY_RIGHT_SHIFT);
|
||||
if (click) { // Switch mode.
|
||||
cursor = mouse.pos;
|
||||
// Reset selection on right click.
|
||||
if (right_click) {
|
||||
verbs.clear_selection(&level, 1);
|
||||
}
|
||||
// Set position.
|
||||
mouse.start_pos = mouse.pos;
|
||||
mouse.mode = if (mod_rect_sel)
|
||||
// If right click then un<mode>
|
||||
mouse.mode = if (right_click and mod_rect_sel)
|
||||
Mouse.MouseMode.unrect_sel
|
||||
else if (right_click and !mod_rect_sel)
|
||||
Mouse.MouseMode.unsel
|
||||
else if (mod_rect_sel)
|
||||
Mouse.MouseMode.rect_sel
|
||||
else
|
||||
Mouse.MouseMode.sel;
|
||||
}
|
||||
},
|
||||
// Select stuff under the cursor.
|
||||
.sel => {
|
||||
.sel, .unsel => {
|
||||
cursor = mouse.pos;
|
||||
level.select_cell(mouse.pos, true);
|
||||
level.select_cell(mouse.pos, mouse.mode == .sel);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
@ -268,9 +266,17 @@ pub fn main() void {
|
|||
level.draw(scale, camera);
|
||||
level.draw_selection(scale, camera);
|
||||
draw.cursor(scale, camera, cursor, mode);
|
||||
if (mode == Mode.rectangle)
|
||||
draw.rectangle_selection(scale, camera, Rect.init_from_vec2(cursor, cursor_before));
|
||||
if (mode == Mode.rectangle or mode == Mode.unrectangle)
|
||||
draw.rectangle_selection(scale, camera, Rect.init_from_vec2(cursor, cursor_before), mode == .rectangle);
|
||||
if (conf.mouse_enabled)
|
||||
mouse.draw(scale, camera);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_key_to_buffer(input_buffer: *[input_buffer_len]u32, input_cursor: *u8, key: c_int) void {
|
||||
input_buffer[input_cursor.*] = @intCast(u32, key);
|
||||
input_cursor.* += 1;
|
||||
// Avoid writing out of memory.
|
||||
if (input_cursor.* >= input_buffer_len)
|
||||
input_cursor.* = input_buffer_len - 1;
|
||||
}
|
||||
|
|
|
@ -8,5 +8,7 @@ pub const Mode = enum {
|
|||
normal, // move cursor and do stuff
|
||||
select, // same, but select while doing so
|
||||
rectangle, // same, but select as a rectangle
|
||||
unselect, // remove from selection instead of adding to it
|
||||
unrectangle, // same as above for rectangle
|
||||
camera, // move camera instead
|
||||
};
|
||||
|
|
|
@ -20,6 +20,8 @@ pub const MouseMode = enum {
|
|||
idle,
|
||||
sel,
|
||||
rect_sel,
|
||||
unsel,
|
||||
unrect_sel,
|
||||
};
|
||||
|
||||
mode: MouseMode, // State.
|
||||
|
@ -38,16 +40,19 @@ pub fn init() Self {
|
|||
/// Draw cursor or preview rectangle selection depending on mode.
|
||||
pub fn draw(self: *Self, scale: scaling.scale_type, offset: Vec2) void {
|
||||
switch (self.mode) {
|
||||
MouseMode.sel => {
|
||||
// Cell size cursor.
|
||||
.idle, .wait, .sel, .unsel => {
|
||||
const x = (self.pos.x - offset.y) * scale;
|
||||
const y = (self.pos.y - offset.y) * scale;
|
||||
ray.DrawRectangleLines(x, y, scale, scale, conf.theme.mode.select);
|
||||
ray.DrawRectangleLines(x, y, scale, scale, switch (self.mode) {
|
||||
.sel => conf.theme.mode.select,
|
||||
.unsel => conf.theme.mode.unselect,
|
||||
else => conf.theme.mode.normal,
|
||||
});
|
||||
},
|
||||
MouseMode.rect_sel => {
|
||||
.rect_sel, .unrect_sel => {
|
||||
const rect = Rect.init_from_vec2(self.pos, self.start_pos);
|
||||
draw_fn.rectangle_selection(scale, offset, rect);
|
||||
draw_fn.rectangle_selection(scale, offset, rect, self.mode == .rect_sel);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue