diff --git a/README.md b/README.md index d3f9c72..81d5f7f 100644 --- a/README.md +++ b/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: * ``: 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 diff --git a/src/actions.zig b/src/actions.zig index f5d4b6c..d28b646 100644 --- a/src/actions.zig +++ b/src/actions.zig @@ -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, diff --git a/src/conf.zig b/src/conf.zig index 56e211a..dddcc33 100644 --- a/src/conf.zig +++ b/src/conf.zig @@ -37,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, }, }; diff --git a/src/draw.zig b/src/draw.zig index 7e0c7f9..c54cbd2 100644 --- a/src/draw.zig +++ b/src/draw.zig @@ -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); } diff --git a/src/main.zig b/src/main.zig index ef3473a..eeffc59 100644 --- a/src/main.zig +++ b/src/main.zig @@ -111,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 { @@ -158,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}), @@ -183,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,12 +219,12 @@ pub fn main() void { // 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 = Mouse.MouseMode.wait; + 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); } } @@ -235,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 + 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 => {}, } @@ -263,8 +266,8 @@ 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); } diff --git a/src/modes.zig b/src/modes.zig index 56394f0..b8ba5f1 100644 --- a/src/modes.zig +++ b/src/modes.zig @@ -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 }; diff --git a/src/mouse.zig b/src/mouse.zig index d45513e..978875b 100644 --- a/src/mouse.zig +++ b/src/mouse.zig @@ -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 => {}, } } -