diff --git a/lua/keyseer/init.lua b/lua/keyseer/init.lua index ac4116b..fc936e3 100644 --- a/lua/keyseer/init.lua +++ b/lua/keyseer/init.lua @@ -64,7 +64,6 @@ KeySeer.config = { -- Initial neovim mode to display keybindings initial_mode = "n", - -- TODO: Represent modifier toggling in highlights include_modifiers = false, -- Boolean to include built in keymaps in display include_builtin_keymaps = false, @@ -72,6 +71,9 @@ KeySeer.config = { include_global_keymaps = true, -- Boolean to include buffer keymaps in display include_buffer_keymaps = true, + -- TODO: Represent modifier toggling in highlights + -- Boolean to include modified keys (e.g. or or C) in display + include_modified_keypresses = false, -- Configuration for ui: -- - `height` and `width` are maximum dimensions. diff --git a/lua/keyseer/keyboard/button.lua b/lua/keyseer/keyboard/button.lua index b67ff32..91a59fc 100644 --- a/lua/keyseer/keyboard/button.lua +++ b/lua/keyseer/keyboard/button.lua @@ -85,7 +85,9 @@ function Button:new(keycap, keycode, row_index, padding_box, highlight_box) keycode = keycode, left_pad = left_pad, right_pad = right_pad, + top_row = row - top_pad, row = row, + bottom_row = row + bottom_pad, width = left_pad + keycap_width + right_pad, is_modifier = modifiers[keycode] or false, _keycap_width = keycap_width, @@ -189,6 +191,13 @@ function Button:set_button_byte_position(col) self.right_byte_col = col + self.width end +---Set the start column for the button +---@param col integer The start column +function Button:set_button_start_col(col) + self.left_col = col + self.right_col = col + self.width +end + ---Highlight a buffer ---@param bufnr buffer A buffer ---@param namespace number The namespace for the highlights diff --git a/lua/keyseer/keyboard/init.lua b/lua/keyseer/keyboard/init.lua index 4aa6c1e..814bd1c 100644 --- a/lua/keyseer/keyboard/init.lua +++ b/lua/keyseer/keyboard/init.lua @@ -5,6 +5,7 @@ -- Each button on the keyboard can be highlighted local Button = require("keyseer.keyboard.button") local Utils = require("keyseer.utils") +local D = require("keyseer.util.debug") -- Border characters for buttons local _borders = { @@ -72,7 +73,7 @@ end ---@field private _shifted_buttons Button[] A mapping table from keycode to button when shift is pressed ---@field private _normal_lines string[] The string representation of the keyboard without shift pressed ---@field private _shifted_lines string[] The string representation of the keyboard with shift pressed ----@field private _locations Button[] A table of button positions +---@field private _locations Button[] A table of buttons for their position local Keyboard = {} Keyboard.__index = Keyboard @@ -361,44 +362,55 @@ end ---Return button at coordinates ---@param row integer ---@param col integer +---@return Button ret the button at the position function Keyboard:get_keycap_at_position(row, col) -- We need to convert the row to a row index -- Each row is keycap_height + top_padding + bottom_padding tall -- We need to convert the col to a column index -- Converting a column back to a column index is hard because the buttons are different width - print("Looking for " .. row .. " and " .. col) - print("Not yet implemented") + local ret = nil + D.log("Keyboard", "Looking for keycap under " .. row .. ", " .. col) + for _, keycaps in pairs(self._locations) do + for _, button in pairs(keycaps) do + D.log("Keyboard", string.format("Checking button: %s", button.keycap)) + if button.top_row <= row and button.bottom_row >= row then + if button.left_col < col and button.right_col >= col then + if not ret then + ret = button + else + Utils.notify("Found multiple matching buttons!") + end + end + end + D.log( + "Keyboard", + string.format( + "%s: (%d, %d) and (%d, %d)", + button.keycap, + button.top_row, + button.bottom_row, + button.left_col, + button.right_col + ) + ) + end + end + if ret then + Utils.notify(string.format("Found %s!", ret.keycap)) + end + return ret end ---Populate lines in a display ---@param ui KeySeerUI The UI to display to ---@param keycaps KeyMapTreeNode The keycaps to display +---@return number height The height of the window +---@return number width The width of the window function Keyboard:populate_lines(ui, keycaps) - -- generate a string representation of the layout, e.g. - -- ┌──────┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬─┬──────┐ - -- │ ` │1│2│3│4│5│6│7│8│9│0│-│=│ │ - -- ├──────┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼──────┤ - -- │ │q│w│e│r│t│y│u│i│o│p│[│]│ \ │ - -- ├──────┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┴──────┤ - -- ││a│s│d│f│g│h│j│k│l│;│'│ │ - -- ├──────┴─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼─┼────────┤ - -- │ │z│x│c│v│b│n│m│,│.│/│ │ - -- └────────┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴────────┘ - -- ┌───────┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬──────┐ - -- │ ` │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 0 │ - │ = │ │ - -- ├───────┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼──────┤ - -- │ │ q │ w │ e │ r │ t │ y │ u │ i │ o │ p │ [ │ ] │ \ │ - -- ├───────┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴┬──┴──────┤ - -- │ │ a │ s │ d │ f │ g │ h │ j │ k │ l │ ; │ ' │ │ - -- ├────────┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─┬─┴─────────┤ - -- │ │ z │ x │ c │ v │ b │ n │ m │ , │ . │ / │ │ - -- └──────────┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───────────┘ local display = ui.render local shift_pressed = ui.state.modifiers.shift local button_lookup = Utils.default_table() - if shift_pressed then - button_lookup = self._shifted_buttons - end + self._locations = button_lookup local rows = {} local keycap_separator_columns = {} @@ -459,6 +471,7 @@ function Keyboard:populate_lines(ui, keycaps) local row_text = _borders["ss "] keycap_separator_columns[row_index] = {} for _, button in ipairs(row) do + button:set_button_start_col(row_length + 1) row_text = row_text .. tostring(button) .. _borders["ss "] local start_col, _ = Utils.get_str_bytes(row_text, row_length + 1, row_length + button.width) button:set_button_byte_position(start_col) @@ -534,5 +547,7 @@ function Keyboard:populate_lines(ui, keycaps) end display:nl() end + D.log("Keyboard", string.format("Columns: %d", display:col())) + return display:row(), display:col() end return Keyboard diff --git a/lua/keyseer/ui/init.lua b/lua/keyseer/ui/init.lua index 9a28e1b..6d9cdbd 100644 --- a/lua/keyseer/ui/init.lua +++ b/lua/keyseer/ui/init.lua @@ -44,6 +44,18 @@ function M.visible() return M.ui and M.ui.win and vim.api.nvim_win_is_valid(M.ui.win) end +local function get_button_under_cursor(ui) + local cursorposition = vim.fn.getcursorcharpos(ui.win) + local row, col = cursorposition[2], cursorposition[3] + -- size of the title is statically calculated + local row_offset = 4 + D.log("UI", "Button row, col: " .. row - row_offset .. ", " .. col) + ---@type Keyboard + local keyboard = ui.state.keyboard + local button = keyboard:get_keycap_at_position(row - row_offset, col) + return button +end + ---@param pane? string The starting pane ---@param mode? string The neovim mode for keymaps ---@param bufnr? integer The buffer for keymaps @@ -80,7 +92,7 @@ function M.create(bufnr) for k, v in pairs(UIConfig.panes) do self:on_key(v["key"], function() if self.state.pane == "home" then - local button = self.render:get_button() + local button = get_button_under_cursor(self) if button then self.state.button = button end @@ -112,23 +124,6 @@ function M.create(bufnr) end end - -- open details for the keycap under the cursor - self:on_key(UIConfig.keys.details, function() - if self.state.pane == "home" then - local button = self.render:get_button() - if button then - if button.is_modifier then - self.state.modifiers[button.keycode] = not self.state.modifiers[button.keycode] - else - self.state.button = button - self.state.prev_pane = self.state.pane - self.state.pane = "details" - end - self:update() - end - end - end) - return self end diff --git a/lua/keyseer/ui/panes/home.lua b/lua/keyseer/ui/panes/home.lua index 81ae5fc..3996887 100644 --- a/lua/keyseer/ui/panes/home.lua +++ b/lua/keyseer/ui/panes/home.lua @@ -22,7 +22,15 @@ local M = { function M.render(ui) local current_keycaps = ui.state.keymaps:get_current_keycaps(ui.state.modifiers) - ui.state.keyboard:populate_lines(ui, current_keycaps) + local height, width = ui.state.keyboard:populate_lines(ui, current_keycaps) + D.log("UI", string.format("Resizing to %dx%d (HxW)", height, width)) + ui.win_opts.height = height - 1 + ui.opts.size.height = height - 1 + if width ~= 0 then + ui.win_opts.width = width + ui.opts.size.width = width + end + ui:layout() for _, keypress in pairs(ui.state.current_keymaps) do vim.keymap.del("n", "g" .. keypress, { buffer = ui.buf }) end @@ -37,6 +45,18 @@ function M.render(ui) end end +local function get_button_under_cursor(ui) + local cursorposition = vim.fn.getcursorcharpos(ui.win) + local row, col = cursorposition[2], cursorposition[3] + -- size of the title is statically calculated + local row_offset = 4 + D.log("UI", "Button row, col: " .. row - row_offset .. ", " .. col) + ---@type Keyboard + local keyboard = ui.state.keyboard + local button = keyboard:get_keycap_at_position(row - row_offset, col) + return button +end + ---Update keymaps when entering the pane function M.on_enter(ui) -- for _, keypress in pairs(ui.state.current_keymaps) do @@ -51,6 +71,21 @@ function M.on_enter(ui) ui.state.keymaps:pop() ui:update() end, { buffer = ui.buf }) + + -- open details for the keycap under the cursor + vim.keymap.set("n", UIConfig.keys.details, function() + local button = get_button_under_cursor(ui) + if button then + if button.is_modifier then + ui.state.modifiers[button.keycode] = not ui.state.modifiers[button.keycode] + else + ui.state.button = button + ui.state.prev_pane = ui.state.pane + ui.state.pane = "details" + end + ui:update() + end + end, { buffer = ui.buf }) end ---Update keymaps when exiting the pane @@ -61,6 +96,7 @@ function M.on_exit(ui) end ui.state.current_keymaps = {} vim.keymap.del("n", UIConfig.keys.back, { buffer = ui.buf }) + vim.keymap.del("n", UIConfig.keys.details, { buffer = ui.buf }) end return M diff --git a/lua/keyseer/ui/render.lua b/lua/keyseer/ui/render.lua index 5a18264..3c071f4 100644 --- a/lua/keyseer/ui/render.lua +++ b/lua/keyseer/ui/render.lua @@ -69,15 +69,4 @@ function M:title() self:nl():nl() end -function M:get_button() - if not (self.ui.win and vim.api.nvim_win_is_valid(self.ui.win)) then - return - end - if not (self.ui.state.pane == "home") then - return - end - local cursorposition = vim.fn.getcursorcharpos(self.ui.win) - local row, col = cursorposition[1], cursorposition[2] -end - return M diff --git a/lua/keyseer/utils.lua b/lua/keyseer/utils.lua index 2c67284..f3f5712 100644 --- a/lua/keyseer/utils.lua +++ b/lua/keyseer/utils.lua @@ -230,7 +230,7 @@ function Utils.default_table() }) end ----Get the start column and end column +---Get the start column and end column in bytes ---@param line string The string of characters to find byte positions ---@param from number The start column ---@param to number The end column