This is a text selection implementation for Dear ImGui, originally part of WhaleConnect.
Released under the MIT License.
- Double-click: Select word
- Triple-click: Select line
- Shift-click: Select range
- Keyboard shortcuts for copy (Ctrl+C/Cmd+C) and select all (Ctrl+A/Cmd+A)
- Automatic scrolling for selecting text outside the window's visible area
- Integration in context menus
- UTF-8 text support
- Dear ImGui
- utfcpp (For UTF-8 handling)
- C++20 (For
std::midpoint
, though this can easily be patched to support C++17)
Copy textselect.cpp
and textselect.hpp
into your project, and update your build settings to compile textselect.cpp
. Also, copy utfcpp into your project so it can be included as <utf8.h>
. (utfcpp is a header-only library so it does not need to be compiled.)
To apply text selection to a window:
#include "textselect.hpp"
- Create a
TextSelect
instance for your window - Call
.update()
on yourTextSelect
instance in your window's render loop
See below for an example.
- Only left-to-right text is supported
- Double-click selection only handles word boundary characters in Latin Unicode blocks
- Each line must be the same height (word wrapping is not supported)
- You should have
ImGuiWindowFlags_NoMove
set in either your window or a child window containing the text so mouse drags can be used to select text instead of moving the window - The accessor functions (
getLineAtIdx
,getNumLines
) should not contain side effects or heavy computations as they can potentially be called multiple times per frame
ImGuiTextSelect works well for text-only windows such as a console/log output or code display.
Some discussion on highlightable text in Dear ImGui: GitHub issue
See the example code for a full program using ImGuiTextSelect. The example is compiled with the xmake build system.
#include <string_view>
#include <vector>
#include "textselect.hpp"
// ---------- At the beginning of your program: ----------
// The lines to show in the window
// You will need to supply TextSelect instances with functions that:
// 1. Take a line number (starting from 0) and return that line in an std::string_view
// 2. Return the total number of lines in the window
// A vector is a convenient way to fulfill the above requirements, but you may use whatever you like.
std::vector<std::string_view> lines{
"Line 1",
"Line 2",
"Line 3",
"A longer line",
"Text selection in Dear ImGui",
"UTF-8 characters Ë ⑤ 三【 】┌──┐"
};
std::string_view getLineAtIdx(size_t idx) {
return lines[idx];
}
size_t getNumLines() {
return lines.size();
}
// Create a TextSelect instance
TextSelect textSelect{ getLineAtIdx, getNumLines };
// ---------- In the main render loop: ----------
// Create a window to contain the text
ImGui::SetNextWindowSize({ 300, 200 });
ImGui::Begin("Text selection");
// Create a child window with the "NoMove" flag
// This allows mouse drags to select text (instead of moving the window), while still
// allowing the window to be moved from the title bar.
ImGui::BeginChild("text", {}, 0, ImGuiWindowFlags_NoMove);
// Display each line
for (const auto& line : lines) ImGui::TextUnformatted(line.c_str());
// Update TextSelect instance (all text selection is handled in this method)
textSelect.update();
// Register a context menu (optional)
// The TextSelect class provides the hasSelection, copy, and selectAll methods
// for manual control.
if (ImGui::BeginPopupContextWindow()) {
ImGui::BeginDisabled(!textSelect.hasSelection());
if (ImGui::MenuItem("Copy", "Ctrl+C")) textSelect.copy();
ImGui::EndDisabled();
if (ImGui::MenuItem("Select all", "Ctrl+A")) textSelect.selectAll();
ImGui::EndPopup();
}
ImGui::EndChild();
ImGui::End();