diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b24847..4f8ab83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(kte) include(GNUInstallDirs) set(CMAKE_CXX_STANDARD 17) -set(KTE_VERSION "1.2.1") +set(KTE_VERSION "1.2.2") # Default to terminal-only build to avoid SDL/OpenGL dependency by default. # Enable with -DBUILD_GUI=ON when SDL2/OpenGL/Freetype are available. @@ -16,36 +16,36 @@ option(KTE_UNDO_DEBUG "Enable undo instrumentation logs" OFF) option(KTE_ENABLE_TREESITTER "Enable optional Tree-sitter highlighter adapter" OFF) if (CMAKE_HOST_UNIX) - message(STATUS "Build system is POSIX.") + message(STATUS "Build system is POSIX.") else () - message(STATUS "Build system is NOT POSIX.") + message(STATUS "Build system is NOT POSIX.") endif () if (MSVC) - add_compile_options("/W4" "$<$:/O2>") + add_compile_options("/W4" "$<$:/O2>") else () - add_compile_options( - "-Wall" - "-Wextra" - "-Werror" - "$<$:-g>" - "$<$:-O2>") - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_compile_options("-stdlib=libc++") - else () - # nothing special for gcc at the moment - endif () + add_compile_options( + "-Wall" + "-Wextra" + "-Werror" + "$<$:-g>" + "$<$:-O2>") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + add_compile_options("-stdlib=libc++") + else () + # nothing special for gcc at the moment + endif () endif () add_compile_definitions(KGE_PLATFORM=${CMAKE_HOST_SYSTEM_NAME}) add_compile_definitions(KTE_VERSION_STR="v${KTE_VERSION}") if (KTE_ENABLE_TREESITTER) - add_compile_definitions(KTE_ENABLE_TREESITTER) + add_compile_definitions(KTE_ENABLE_TREESITTER) endif () message(STATUS "Build system: ${CMAKE_HOST_SYSTEM_NAME}") if (${BUILD_GUI}) - include(cmake/imgui.cmake) + include(cmake/imgui.cmake) endif () # NCurses for terminal mode @@ -55,250 +55,250 @@ find_package(Curses REQUIRED) include_directories(${CURSES_INCLUDE_DIR}) set(SYNTAX_SOURCES - syntax/GoHighlighter.cc - syntax/CppHighlighter.cc - syntax/JsonHighlighter.cc - syntax/ErlangHighlighter.cc - syntax/MarkdownHighlighter.cc - syntax/TreeSitterHighlighter.cc - syntax/LispHighlighter.cc - syntax/HighlighterEngine.cc - syntax/RustHighlighter.cc - syntax/HighlighterRegistry.cc - syntax/SqlHighlighter.cc - syntax/NullHighlighter.cc - syntax/ForthHighlighter.cc - syntax/PythonHighlighter.cc - syntax/ShellHighlighter.cc + syntax/GoHighlighter.cc + syntax/CppHighlighter.cc + syntax/JsonHighlighter.cc + syntax/ErlangHighlighter.cc + syntax/MarkdownHighlighter.cc + syntax/TreeSitterHighlighter.cc + syntax/LispHighlighter.cc + syntax/HighlighterEngine.cc + syntax/RustHighlighter.cc + syntax/HighlighterRegistry.cc + syntax/SqlHighlighter.cc + syntax/NullHighlighter.cc + syntax/ForthHighlighter.cc + syntax/PythonHighlighter.cc + syntax/ShellHighlighter.cc ) if (KTE_ENABLE_TREESITTER) - list(APPEND SYNTAX_SOURCES - TreeSitterHighlighter.cc) + list(APPEND SYNTAX_SOURCES + TreeSitterHighlighter.cc) endif () set(COMMON_SOURCES - GapBuffer.cc - PieceTable.cc - Buffer.cc - Editor.cc - Command.cc - HelpText.cc - KKeymap.cc - TerminalInputHandler.cc - TerminalRenderer.cc - TerminalFrontend.cc - TestInputHandler.cc - TestRenderer.cc - TestFrontend.cc - UndoNode.cc - UndoTree.cc - UndoSystem.cc + GapBuffer.cc + PieceTable.cc + Buffer.cc + Editor.cc + Command.cc + HelpText.cc + KKeymap.cc + TerminalInputHandler.cc + TerminalRenderer.cc + TerminalFrontend.cc + TestInputHandler.cc + TestRenderer.cc + TestFrontend.cc + UndoNode.cc + UndoTree.cc + UndoSystem.cc - ${SYNTAX_SOURCES} + ${SYNTAX_SOURCES} ) set(SYNTAX_HEADERS - syntax/GoHighlighter.h - syntax/HighlighterEngine.h - syntax/ShellHighlighter.h - syntax/MarkdownHighlighter.h - syntax/LispHighlighter.h - syntax/SqlHighlighter.h - syntax/ForthHighlighter.h - syntax/JsonHighlighter.h - syntax/TreeSitterHighlighter.h - syntax/NullHighlighter.h - syntax/CppHighlighter.h - syntax/ErlangHighlighter.h - syntax/LanguageHighlighter.h - syntax/RustHighlighter.h - syntax/PythonHighlighter.h + syntax/GoHighlighter.h + syntax/HighlighterEngine.h + syntax/ShellHighlighter.h + syntax/MarkdownHighlighter.h + syntax/LispHighlighter.h + syntax/SqlHighlighter.h + syntax/ForthHighlighter.h + syntax/JsonHighlighter.h + syntax/TreeSitterHighlighter.h + syntax/NullHighlighter.h + syntax/CppHighlighter.h + syntax/ErlangHighlighter.h + syntax/LanguageHighlighter.h + syntax/RustHighlighter.h + syntax/PythonHighlighter.h ) if (KTE_ENABLE_TREESITTER) - list(APPEND THEME_HEADERS - TreeSitterHighlighter.h) + list(APPEND THEME_HEADERS + TreeSitterHighlighter.h) endif () set(THEME_HEADERS - themes/ThemeHelpers.h - themes/EInk.h - themes/Gruvbox.h - themes/Solarized.h - themes/Plan9.h - themes/Nord.h + themes/ThemeHelpers.h + themes/EInk.h + themes/Gruvbox.h + themes/Solarized.h + themes/Plan9.h + themes/Nord.h ) set(COMMON_HEADERS - GapBuffer.h - PieceTable.h - Buffer.h - Editor.h - AppendBuffer.h - Command.h - HelpText.h - KKeymap.h - InputHandler.h - TerminalInputHandler.h - Renderer.h - TerminalRenderer.h - Frontend.h - TerminalFrontend.h - TestInputHandler.h - TestRenderer.h - TestFrontend.h - UndoNode.h - UndoTree.h - UndoSystem.h - Highlight.h + GapBuffer.h + PieceTable.h + Buffer.h + Editor.h + AppendBuffer.h + Command.h + HelpText.h + KKeymap.h + InputHandler.h + TerminalInputHandler.h + Renderer.h + TerminalRenderer.h + Frontend.h + TerminalFrontend.h + TestInputHandler.h + TestRenderer.h + TestFrontend.h + UndoNode.h + UndoTree.h + UndoSystem.h + Highlight.h - ${SYNTAX_HEADERS} - ${THEME_HEADERS} + ${SYNTAX_HEADERS} + ${THEME_HEADERS} ) # kte (terminal-first) executable add_executable(kte - main.cc - ${COMMON_SOURCES} - ${COMMON_HEADERS} + main.cc + ${COMMON_SOURCES} + ${COMMON_HEADERS} ) if (KTE_USE_PIECE_TABLE) - target_compile_definitions(kte PRIVATE KTE_USE_PIECE_TABLE=1) + target_compile_definitions(kte PRIVATE KTE_USE_PIECE_TABLE=1) endif () if (KTE_UNDO_DEBUG) - target_compile_definitions(kte PRIVATE KTE_UNDO_DEBUG=1) + target_compile_definitions(kte PRIVATE KTE_UNDO_DEBUG=1) endif () target_link_libraries(kte ${CURSES_LIBRARIES}) if (KTE_ENABLE_TREESITTER) - # Users can provide their own tree-sitter include/lib via cache variables - set(TREESITTER_INCLUDE_DIR "" CACHE PATH "Path to tree-sitter include directory") - set(TREESITTER_LIBRARY "" CACHE FILEPATH "Path to tree-sitter library (.a/.dylib)") - if (TREESITTER_INCLUDE_DIR) - target_include_directories(kte PRIVATE ${TREESITTER_INCLUDE_DIR}) - endif () - if (TREESITTER_LIBRARY) - target_link_libraries(kte ${TREESITTER_LIBRARY}) - endif () + # Users can provide their own tree-sitter include/lib via cache variables + set(TREESITTER_INCLUDE_DIR "" CACHE PATH "Path to tree-sitter include directory") + set(TREESITTER_LIBRARY "" CACHE FILEPATH "Path to tree-sitter library (.a/.dylib)") + if (TREESITTER_INCLUDE_DIR) + target_include_directories(kte PRIVATE ${TREESITTER_INCLUDE_DIR}) + endif () + if (TREESITTER_LIBRARY) + target_link_libraries(kte ${TREESITTER_LIBRARY}) + endif () endif () install(TARGETS kte - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) # Man pages install(FILES docs/kte.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) if (BUILD_TESTS) - # test_undo executable for testing undo/redo system - add_executable(test_undo - test_undo.cc - ${COMMON_SOURCES} - ${COMMON_HEADERS} - ) + # test_undo executable for testing undo/redo system + add_executable(test_undo + test_undo.cc + ${COMMON_SOURCES} + ${COMMON_HEADERS} + ) - if (KTE_USE_PIECE_TABLE) - target_compile_definitions(test_undo PRIVATE KTE_USE_PIECE_TABLE=1) - endif () + if (KTE_USE_PIECE_TABLE) + target_compile_definitions(test_undo PRIVATE KTE_USE_PIECE_TABLE=1) + endif () - if (KTE_UNDO_DEBUG) - target_compile_definitions(test_undo PRIVATE KTE_UNDO_DEBUG=1) - endif () + if (KTE_UNDO_DEBUG) + target_compile_definitions(test_undo PRIVATE KTE_UNDO_DEBUG=1) + endif () - target_link_libraries(test_undo ${CURSES_LIBRARIES}) - if (KTE_ENABLE_TREESITTER) - if (TREESITTER_INCLUDE_DIR) - target_include_directories(test_undo PRIVATE ${TREESITTER_INCLUDE_DIR}) - endif () - if (TREESITTER_LIBRARY) - target_link_libraries(test_undo ${TREESITTER_LIBRARY}) - endif () - endif () + target_link_libraries(test_undo ${CURSES_LIBRARIES}) + if (KTE_ENABLE_TREESITTER) + if (TREESITTER_INCLUDE_DIR) + target_include_directories(test_undo PRIVATE ${TREESITTER_INCLUDE_DIR}) + endif () + if (TREESITTER_LIBRARY) + target_link_libraries(test_undo ${TREESITTER_LIBRARY}) + endif () + endif () endif () if (${BUILD_GUI}) - target_sources(kte PRIVATE - Font.h - GUIConfig.cc - GUIConfig.h - GUIRenderer.cc - GUIRenderer.h - GUIInputHandler.cc - GUIInputHandler.h - GUIFrontend.cc - GUIFrontend.h) - target_compile_definitions(kte PRIVATE KTE_BUILD_GUI=1) - target_link_libraries(kte imgui) + target_sources(kte PRIVATE + Font.h + GUIConfig.cc + GUIConfig.h + GUIRenderer.cc + GUIRenderer.h + GUIInputHandler.cc + GUIInputHandler.h + GUIFrontend.cc + GUIFrontend.h) + target_compile_definitions(kte PRIVATE KTE_BUILD_GUI=1) + target_link_libraries(kte imgui) - # kge (GUI-first) executable - add_executable(kge - main.cc - ${COMMON_SOURCES} - ${COMMON_HEADERS} - GUIConfig.cc - GUIConfig.h - GUIRenderer.cc - GUIRenderer.h - GUIInputHandler.cc - GUIInputHandler.h - GUIFrontend.cc - GUIFrontend.h) - target_compile_definitions(kge PRIVATE KTE_BUILD_GUI=1 KTE_DEFAULT_GUI=1 KTE_FONT_SIZE=${KTE_FONT_SIZE}) - if (KTE_UNDO_DEBUG) - target_compile_definitions(kge PRIVATE KTE_UNDO_DEBUG=1) - endif () - target_link_libraries(kge ${CURSES_LIBRARIES} imgui) + # kge (GUI-first) executable + add_executable(kge + main.cc + ${COMMON_SOURCES} + ${COMMON_HEADERS} + GUIConfig.cc + GUIConfig.h + GUIRenderer.cc + GUIRenderer.h + GUIInputHandler.cc + GUIInputHandler.h + GUIFrontend.cc + GUIFrontend.h) + target_compile_definitions(kge PRIVATE KTE_BUILD_GUI=1 KTE_DEFAULT_GUI=1 KTE_FONT_SIZE=${KTE_FONT_SIZE}) + if (KTE_UNDO_DEBUG) + target_compile_definitions(kge PRIVATE KTE_UNDO_DEBUG=1) + endif () + target_link_libraries(kge ${CURSES_LIBRARIES} imgui) - # On macOS, build kge as a proper .app bundle - if (APPLE) - # Define the icon file - set(MACOSX_BUNDLE_ICON_FILE kge.icns) - set(kge_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}") + # On macOS, build kge as a proper .app bundle + if (APPLE) + # Define the icon file + set(MACOSX_BUNDLE_ICON_FILE kge.icns) + set(kge_ICON "${CMAKE_CURRENT_SOURCE_DIR}/${MACOSX_BUNDLE_ICON_FILE}") - # Add icon to the target sources and mark it as a resource - target_sources(kge PRIVATE ${kge_ICON}) - set_source_files_properties(${kge_ICON} PROPERTIES - MACOSX_PACKAGE_LOCATION Resources) + # Add icon to the target sources and mark it as a resource + target_sources(kge PRIVATE ${kge_ICON}) + set_source_files_properties(${kge_ICON} PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) - # Configure Info.plist with version and identifiers - set(KGE_BUNDLE_ID "dev.wntrmute.kge") - configure_file( - ${CMAKE_CURRENT_LIST_DIR}/cmake/Info.plist.in - ${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist - @ONLY) + # Configure Info.plist with version and identifiers + set(KGE_BUNDLE_ID "dev.wntrmute.kge") + configure_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/Info.plist.in + ${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist + @ONLY) - set_target_properties(kge PROPERTIES - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID} - MACOSX_BUNDLE_BUNDLE_NAME "kge" - MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} - MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist") + set_target_properties(kge PROPERTIES + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER ${KGE_BUNDLE_ID} + MACOSX_BUNDLE_BUNDLE_NAME "kge" + MACOSX_BUNDLE_ICON_FILE ${MACOSX_BUNDLE_ICON_FILE} + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_BINARY_DIR}/kge-Info.plist") - add_dependencies(kge kte) - add_custom_command(TARGET kge POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - $ - $/kte - COMMENT "Copying kte binary into kge.app bundle") + add_dependencies(kge kte) + add_custom_command(TARGET kge POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + $ + $/kte + COMMENT "Copying kte binary into kge.app bundle") - install(TARGETS kge - BUNDLE DESTINATION . - ) + install(TARGETS kge + BUNDLE DESTINATION . + ) - install(TARGETS kte - RUNTIME DESTINATION kge.app/Contents/MacOS - ) - else () - install(TARGETS kge - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - ) - endif () - # Install kge man page only when GUI is built - install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) - install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons) + install(TARGETS kte + RUNTIME DESTINATION kge.app/Contents/MacOS + ) + else () + install(TARGETS kge + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + endif () + # Install kge man page only when GUI is built + install(FILES docs/kge.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + install(FILES kge.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/icons) endif () diff --git a/Command.cc b/Command.cc index 0b239bf..6f543d5 100644 --- a/Command.cc +++ b/Command.cc @@ -3067,6 +3067,74 @@ cmd_page_down(CommandContext &ctx) } +static bool +cmd_scroll_up(CommandContext &ctx) +{ + Buffer *buf = ctx.editor.CurrentBuffer(); + if (!buf) + return false; + ensure_at_least_one_line(*buf); + const auto &rows = buf->Rows(); + std::size_t content_rows = std::max(1, ctx.editor.ContentRows()); + std::size_t rowoffs = buf->Rowoffs(); + + // Scroll up by 3 lines (or count if specified), without moving cursor + int scroll_amount = ctx.count > 0 ? ctx.count : 3; + if (rowoffs >= static_cast(scroll_amount)) + rowoffs -= static_cast(scroll_amount); + else + rowoffs = 0; + + buf->SetOffsets(rowoffs, buf->Coloffs()); + + // If cursor is now below the visible area, move it to the last visible line + std::size_t cury = buf->Cury(); + if (cury >= rowoffs + content_rows) { + std::size_t new_y = rowoffs + content_rows - 1; + if (new_y >= rows.size() && !rows.empty()) + new_y = rows.size() - 1; + buf->SetCursor(buf->Curx(), new_y); + } + + return true; +} + + +static bool +cmd_scroll_down(CommandContext &ctx) +{ + Buffer *buf = ctx.editor.CurrentBuffer(); + if (!buf) + return false; + ensure_at_least_one_line(*buf); + const auto &rows = buf->Rows(); + std::size_t content_rows = std::max(1, ctx.editor.ContentRows()); + std::size_t rowoffs = buf->Rowoffs(); + + // Scroll down by 3 lines (or count if specified), without moving cursor + int scroll_amount = ctx.count > 0 ? ctx.count : 3; + + // Compute maximum top offset + std::size_t max_top = 0; + if (!rows.empty() && rows.size() > content_rows) + max_top = rows.size() - content_rows; + + rowoffs += static_cast(scroll_amount); + if (rowoffs > max_top) + rowoffs = max_top; + + buf->SetOffsets(rowoffs, buf->Coloffs()); + + // If cursor is now above the visible area, move it to the first visible line + std::size_t cury = buf->Cury(); + if (cury < rowoffs) { + buf->SetCursor(buf->Curx(), rowoffs); + } + + return true; +} + + static inline bool is_word_char(unsigned char c) { @@ -3675,6 +3743,8 @@ InstallDefaultCommands() CommandRegistry::Register({CommandId::MoveEnd, "end", "Move to end of line", cmd_move_end}); CommandRegistry::Register({CommandId::PageUp, "page-up", "Page up", cmd_page_up}); CommandRegistry::Register({CommandId::PageDown, "page-down", "Page down", cmd_page_down}); + CommandRegistry::Register({CommandId::ScrollUp, "scroll-up", "Scroll viewport up", cmd_scroll_up}); + CommandRegistry::Register({CommandId::ScrollDown, "scroll-down", "Scroll viewport down", cmd_scroll_down}); CommandRegistry::Register({CommandId::WordPrev, "word-prev", "Move to previous word", cmd_word_prev}); CommandRegistry::Register({CommandId::WordNext, "word-next", "Move to next word", cmd_word_next}); CommandRegistry::Register({ diff --git a/Command.h b/Command.h index 20ede02..909d981 100644 --- a/Command.h +++ b/Command.h @@ -58,6 +58,8 @@ enum class CommandId { MoveEnd, PageUp, PageDown, + ScrollUp, // scroll viewport up (towards beginning) without moving cursor + ScrollDown, // scroll viewport down (towards end) without moving cursor WordPrev, WordNext, DeleteWordPrev, // delete previous word (ESC BACKSPACE) diff --git a/GUIInputHandler.cc b/GUIInputHandler.cc index 7aa6b51..ce272f5 100644 --- a/GUIInputHandler.cc +++ b/GUIInputHandler.cc @@ -285,15 +285,11 @@ GUIInputHandler::ProcessSDLEvent(const SDL_Event &e) bool produced = false; switch (e.type) { case SDL_MOUSEWHEEL: { - // If ImGui wants to capture the mouse (e.g., hovering the File Picker list), - // don't translate wheel events into editor scrolling. - // This prevents background buffer scroll while using GUI widgets. - ImGuiIO &io = ImGui::GetIO(); - if (io.WantCaptureMouse) { - return true; // consumed by GUI - } - - // Map vertical wheel to line-wise cursor movement (MoveUp/MoveDown) + // Map vertical wheel to viewport scrolling (ScrollUp/ScrollDown) + // Note: We don't check WantCaptureMouse here because ImGui sets it to true + // whenever the mouse is over any ImGui window (including our editor content area). + // The NoScrollWithMouse flag on the child window prevents ImGui from handling + // scroll internally, so we can safely process wheel events ourselves. int dy = e.wheel.y; #ifdef SDL_MOUSEWHEEL_FLIPPED if (e.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) @@ -301,7 +297,7 @@ GUIInputHandler::ProcessSDLEvent(const SDL_Event &e) #endif if (dy != 0) { int repeat = dy > 0 ? dy : -dy; - CommandId id = dy > 0 ? CommandId::MoveUp : CommandId::MoveDown; + CommandId id = dy > 0 ? CommandId::ScrollUp : CommandId::ScrollDown; std::lock_guard lk(mu_); for (int i = 0; i < repeat; ++i) { q_.push(MappedInput{true, id, std::string(), 0}); @@ -372,7 +368,7 @@ GUIInputHandler::ProcessSDLEvent(const SDL_Event &e) // Digits without shift, or a plain '-' const bool is_digit_key = (key >= SDLK_0 && key <= SDLK_9) && !(mods & KMOD_SHIFT); const bool is_minus_key = (key == SDLK_MINUS); - if (uarg_active_ && uarg_collecting_ && (is_digit_key || is_minus_key)) { + if (uarg_active_ && uarg_collecting_ &&(is_digit_key || is_minus_key)) { suppress_text_input_once_ = true; } } @@ -564,7 +560,12 @@ GUIInputHandler::ProcessSDLEvent(const SDL_Event &e) if (produced && mi.hasCommand) { // Attach universal-argument count if present, then clear the state - if (uarg_active_ && mi.id != CommandId::UArgStatus) { + if (uarg_active_ &&mi + + . + id != CommandId::UArgStatus + ) + { int count = 0; if (!uarg_had_digits_ && !uarg_negative_) { count = (uarg_value_ > 0) ? uarg_value_ : 4; @@ -597,4 +598,4 @@ GUIInputHandler::Poll(MappedInput &out) out = q_.front(); q_.pop(); return true; -} +} \ No newline at end of file diff --git a/TerminalInputHandler.cc b/TerminalInputHandler.cc index f4f1f70..f9ab82b 100644 --- a/TerminalInputHandler.cc +++ b/TerminalInputHandler.cc @@ -35,16 +35,16 @@ map_key_to_command(const int ch, case KEY_MOUSE: { MEVENT ev{}; if (getmouse(&ev) == OK) { - // Mouse wheel → map to MoveUp/MoveDown one line per wheel notch + // Mouse wheel → scroll viewport without moving cursor #ifdef BUTTON4_PRESSED if (ev.bstate & (BUTTON4_PRESSED | BUTTON4_RELEASED | BUTTON4_CLICKED)) { - out = {true, CommandId::MoveUp, "", 0}; + out = {true, CommandId::ScrollUp, "", 0}; return true; } #endif #ifdef BUTTON5_PRESSED if (ev.bstate & (BUTTON5_PRESSED | BUTTON5_RELEASED | BUTTON5_CLICKED)) { - out = {true, CommandId::MoveDown, "", 0}; + out = {true, CommandId::ScrollDown, "", 0}; return true; } #endif @@ -320,4 +320,4 @@ TerminalInputHandler::Poll(MappedInput &out) { out = {}; return decode_(out) && out.hasCommand; -} +} \ No newline at end of file