Started work on TextEdit widget
This commit is contained in:
parent
655d7b1f98
commit
a3eb4ba3cc
17 changed files with 900 additions and 351 deletions
17
build
17
build
|
|
@ -43,5 +43,22 @@ else
|
|||
cd bin
|
||||
./ostd_test
|
||||
cd ..
|
||||
elif [[ "$1" == "dbg" ]]; then
|
||||
printf "\n\033[0;32mDebugging Test Application...\n\n\033[0m"
|
||||
cd bin
|
||||
gdb -batch \
|
||||
-ex 'set pagination off' \
|
||||
-ex 'set print pretty on' \
|
||||
-ex 'set logging file ../gdb-last.log' \
|
||||
-ex 'set logging overwrite on' \
|
||||
-ex 'set logging on' \
|
||||
-ex 'handle SIGPIPE nostop noprint pass' \
|
||||
-ex run \
|
||||
-ex 'bt full' \
|
||||
-ex 'info locals' \
|
||||
-ex 'info args' \
|
||||
-ex quit \
|
||||
--args ./ostd_test
|
||||
cd ..
|
||||
fi
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -388,3 +388,14 @@ macro set_common_values(_borderColor = $borderColor, _fontSize = 18, _showBorder
|
|||
set_focus_border($blueAccent)
|
||||
}
|
||||
% ======================
|
||||
|
||||
|
||||
|
||||
% ====== TextEdit ======
|
||||
(textEdit) {
|
||||
set_common_values()
|
||||
set_focus_border($blueAccent)
|
||||
backgroundColor = $darkColor
|
||||
textColor = $textColor
|
||||
}
|
||||
% ======================
|
||||
|
|
|
|||
|
|
@ -29,6 +29,9 @@
|
|||
***FIX: KeyPress crash on TreeView
|
||||
***FIX: Animated icons not working in TreeView
|
||||
***Check if possible, then change all WindowCore& refs to Window&
|
||||
***Add utf-8 codepoint walking functions to String
|
||||
***Change the stored text in KeyEventData from a single char to the full utf-8 string
|
||||
***Check if Text Entered events are enabled via SDL
|
||||
Implement cursors in Stylesheet
|
||||
FIX: Window getting exponentially bigger when Desktop scale changes
|
||||
FIX: Refreshing scroll when content extent changes
|
||||
|
|
@ -56,17 +59,17 @@ FIX: ContextMenu rendering partly outside the screen when showing it close to th
|
|||
Add ToolBar::addButton overload that takes a ogfx::Icon wrapper
|
||||
Redesign theme overrides to be more robust
|
||||
Add modifiers to Event.keyboard path
|
||||
Implement a font-fallback chain in FontGlyphAtlas
|
||||
Implement appendText in TextBuffer and TextEdit
|
||||
Implement per-glyph caching in BasicRenderer2D
|
||||
Added double-click event to widgets
|
||||
Implement shift-selection in TextEdit (mouse/keyboard)
|
||||
Implement ctrl-selection/navigation in TextEdit
|
||||
Implement home/end and pg up/pg down in TextEdit
|
||||
|
||||
|
||||
|
||||
|
||||
Text:
|
||||
Check if Text Entered events are enabled via SDL
|
||||
Add utf-8 codepoint walking functions to String
|
||||
Change the stored text in KeyEventData from a single char to the full utf-8 string
|
||||
Implement a font-fallback chain in FontGlyphAtlas
|
||||
|
||||
|
||||
|
||||
|
||||
Implement following widgets:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
2076
|
||||
2077
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ namespace ogfx
|
|||
{
|
||||
public: enum class eKeyEvent { Pressed = 0, Released, Text };
|
||||
public:
|
||||
inline KeyEventData(WindowCore& parent, i32 key, char _text, eKeyEvent evt) : parentWindow(parent), keyCode(key), text(_text), eventType(evt)
|
||||
inline KeyEventData(WindowCore& parent, i32 key, const String& _text, eKeyEvent evt) : parentWindow(parent), keyCode(key), text(_text), eventType(evt)
|
||||
{
|
||||
setTypeName("ogfx::KeyEventData");
|
||||
validate();
|
||||
|
|
@ -78,7 +78,7 @@ namespace ogfx
|
|||
|
||||
public:
|
||||
i32 keyCode;
|
||||
char text;
|
||||
String text;
|
||||
eKeyEvent eventType;
|
||||
WindowCore& parentWindow;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
/*
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
|
||||
This file is part of OmniaFramework.
|
||||
This file is part of OmniaFramework.
|
||||
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "RawTextInput.hpp"
|
||||
#include "../gui/Window.hpp"
|
||||
#include "../../io/Logger.hpp"
|
||||
#include "utils/Keycodes.hpp"
|
||||
#include "../utils/Keycodes.hpp"
|
||||
|
||||
namespace ogfx
|
||||
{
|
||||
|
|
@ -55,9 +55,9 @@ namespace ogfx
|
|||
OX_ERROR("Invalid character filter in RawTextInput event listener.");
|
||||
return; //TODO: Better error
|
||||
}
|
||||
if (parent.m_charFilter->isValidChar(data.text))
|
||||
if (parent.m_charFilter->isValidChar(data.text[0]))
|
||||
{
|
||||
if (parent.m_lastChar == data.text && parent.m_keyRepeatCounter.isCounting())
|
||||
if (parent.m_lastChar == data.text[0] && parent.m_keyRepeatCounter.isCounting())
|
||||
{
|
||||
onSignalHandled(signal);
|
||||
return;
|
||||
|
|
@ -66,10 +66,10 @@ namespace ogfx
|
|||
String s2 = "";
|
||||
if (parent.m_cursorPosition < parent.m_text.len())
|
||||
s2 = parent.m_text.new_substr(parent.m_cursorPosition);
|
||||
s1.addChar(data.text).add(s2);
|
||||
s1.addChar(data.text[0]).add(s2);
|
||||
parent.m_text = s1;
|
||||
parent.m_cursorPosition++;
|
||||
parent.m_lastChar = data.text;
|
||||
parent.m_lastChar = data.text[0];
|
||||
parent.m_keyRepeatCounter.start();
|
||||
if (parent.m_theme.cursorBlink)
|
||||
parent.m_theme.cursorBlinkCounter.start();
|
||||
|
|
|
|||
|
|
@ -217,28 +217,6 @@ namespace ogfx
|
|||
}
|
||||
}
|
||||
|
||||
void WidgetManager::onKeyPressed(const Event& event)
|
||||
{
|
||||
auto focused = m_window.getFocusManager().getFocused();
|
||||
if (!focused || !focused->isVisible()) return;
|
||||
focused->__onKeyPressed(event);
|
||||
}
|
||||
|
||||
void WidgetManager::onKeyReleased(const Event& event)
|
||||
{
|
||||
auto focus = m_window.getFocusManager();
|
||||
auto focused = focus.getFocused();
|
||||
if (!focused || !focused->isVisible()) return;
|
||||
focused->__onKeyReleased(event);
|
||||
}
|
||||
|
||||
void WidgetManager::onTextEntered(const Event& event)
|
||||
{
|
||||
auto focused = m_window.getFocusManager().getFocused();
|
||||
if (!focused || !focused->isVisible()) return;
|
||||
focused->__onTextEntered(event);
|
||||
}
|
||||
|
||||
void WidgetManager::onWindowClosed(const Event& event)
|
||||
{
|
||||
for (auto& w : m_widgetList)
|
||||
|
|
|
|||
|
|
@ -50,9 +50,6 @@ namespace ogfx
|
|||
void onMouseMoved(const Event& event);
|
||||
void onMouseScrolled(const Event& event);
|
||||
void onMouseDragged(const Event& event);
|
||||
void onKeyPressed(const Event& event);
|
||||
void onKeyReleased(const Event& event);
|
||||
void onTextEntered(const Event& event);
|
||||
void onWindowClosed(const Event& event);
|
||||
void onWindowResized(const Event& event);
|
||||
void onWindowFocused(const Event& event);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include <ogfx/gui/widgets/Slider.hpp>
|
||||
#include <ogfx/gui/widgets/ComboBox.hpp>
|
||||
#include <ogfx/gui/widgets/TreeView.hpp>
|
||||
#include <ogfx/gui/widgets/Text.hpp>
|
||||
|
||||
namespace ogfx
|
||||
{
|
||||
|
|
|
|||
|
|
@ -617,21 +617,21 @@ namespace ogfx
|
|||
MouseEventData mmd = get_mouse_state(event);
|
||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::MouseReleased, ostd::Signal::Priority::RealTime, mmd);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_TEXT_INPUT)
|
||||
{
|
||||
KeyEventData ked(*this, 0, event.text.text, KeyEventData::eKeyEvent::Text);
|
||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::TextEntered, ostd::Signal::Priority::RealTime, ked);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_KEY_DOWN)
|
||||
{
|
||||
KeyEventData ked(*this, (i32)event.key.key, 0, KeyEventData::eKeyEvent::Pressed);
|
||||
KeyEventData ked(*this, (i32)event.key.key, "", KeyEventData::eKeyEvent::Pressed);
|
||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::KeyPressed, ostd::Signal::Priority::RealTime, ked);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_KEY_UP)
|
||||
{
|
||||
KeyEventData ked(*this, (i32)event.key.key, 0, KeyEventData::eKeyEvent::Released);
|
||||
KeyEventData ked(*this, (i32)event.key.key, "", KeyEventData::eKeyEvent::Released);
|
||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::KeyReleased, ostd::Signal::Priority::RealTime, ked);
|
||||
}
|
||||
else if (event.type == SDL_EVENT_TEXT_INPUT)
|
||||
{
|
||||
KeyEventData ked(*this, 0, event.text.text[0], KeyEventData::eKeyEvent::Text);
|
||||
ostd::SignalHandler::emitSignal(ostd::BuiltinSignals::TextEntered, ostd::Signal::Priority::RealTime, ked);
|
||||
}
|
||||
__on_event(event);
|
||||
}
|
||||
|
||||
|
|
@ -1002,7 +1002,9 @@ namespace ogfx
|
|||
evt.__original_signal_id = ostd::BuiltinSignals::KeyPressed;
|
||||
m_toolbar.__onKeyPressed(evt);
|
||||
m_statusbar.__onKeyPressed(evt);
|
||||
m_rootWidget.__onKeyPressed(evt);
|
||||
auto focused = m_focusManager.getFocused();
|
||||
if (focused)
|
||||
focused->__onKeyPressed(evt);
|
||||
}
|
||||
else if (signal.ID == ostd::BuiltinSignals::KeyReleased)
|
||||
{
|
||||
|
|
@ -1011,7 +1013,9 @@ namespace ogfx
|
|||
m_focusManager.onKeyReleased(evt);
|
||||
m_toolbar.__onKeyReleased(evt);
|
||||
m_statusbar.__onKeyReleased(evt);
|
||||
m_rootWidget.__onKeyReleased(evt);
|
||||
auto focused = m_focusManager.getFocused();
|
||||
if (focused)
|
||||
focused->__onKeyReleased(evt);
|
||||
if (evt.keyboard->keyCode == KeyCode::Escape)
|
||||
close();
|
||||
}
|
||||
|
|
@ -1021,7 +1025,9 @@ namespace ogfx
|
|||
evt.__original_signal_id = ostd::BuiltinSignals::TextEntered;
|
||||
m_toolbar.__onTextEntered(evt);
|
||||
m_statusbar.__onTextEntered(evt);
|
||||
m_rootWidget.__onTextEntered(evt);
|
||||
auto focused = m_focusManager.getFocused();
|
||||
if (focused)
|
||||
focused->__onTextEntered(evt);
|
||||
}
|
||||
onSignal(signal);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,427 @@
|
|||
*/
|
||||
|
||||
#include "Text.hpp"
|
||||
#include "../../utils/Keycodes.hpp"
|
||||
#include "../../render/BasicRenderer.hpp"
|
||||
|
||||
namespace ostd
|
||||
namespace ogfx
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
TextEdit& TextEdit::create(void)
|
||||
{
|
||||
setPadding({ 0, 0, 0, 0 });
|
||||
setTypeName("ogfx::gui::TextEdit");
|
||||
disableChildren();
|
||||
setStylesheetCategoryName("textEdit");
|
||||
enableFocus();
|
||||
enableBackground();
|
||||
enableBorder();
|
||||
enableTextEnteredEvent(true);
|
||||
setSize({ 200, 30 });
|
||||
m_cursorBlinkTimer.start();
|
||||
m_buffer.setOnChanged([this](const ostd::TextBuffer&) {
|
||||
m_layout.dirty = true;
|
||||
});
|
||||
m_buffer.setOnCursorMoved([this](const ostd::TextBuffer&) {
|
||||
if (m_cursorBlink)
|
||||
m_cursorBlinkTimer.start();
|
||||
m_cursorState = true;
|
||||
});
|
||||
validate();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TextEdit::applyTheme(const ostd::Stylesheet& theme)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextEdit::onDraw(ogfx::BasicRenderer2D& gfx)
|
||||
{
|
||||
const auto bounds = getGlobalBounds();
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 1. Rebuild the layout cache if the buffer (or font/size) has
|
||||
// changed since last frame. We mark dirty in the buffer's
|
||||
// onChanged callback (set up in create()).
|
||||
// -------------------------------------------------------------
|
||||
if (m_layout.dirty)
|
||||
rebuild_layout(gfx);
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 2. Make sure the cursor is in view. This adjusts m_scrollX as
|
||||
// needed so the cursor sits inside the widget's content area.
|
||||
// -------------------------------------------------------------
|
||||
ensure_cursor_visible(bounds);
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 3. Compute the text content area inside the widget — the
|
||||
// rectangle where text is allowed to render. Anything drawn
|
||||
// outside this rect gets clipped away.
|
||||
// -------------------------------------------------------------
|
||||
const Rectangle contentArea {
|
||||
bounds.x + m_textPadding.left,
|
||||
bounds.y,
|
||||
bounds.w - m_textPadding.left - m_textPadding.right,
|
||||
bounds.h
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 5. Compute the y position for the text baseline area. Vertically
|
||||
// centered using the cached line height. This is single-line
|
||||
// behavior; for multiline you'd loop over lines and offset y
|
||||
// by lineHeight per row.
|
||||
// -------------------------------------------------------------
|
||||
const f32 textY = bounds.y + (bounds.h - m_layout.lineHeight) * 0.5f;
|
||||
const f32 textX = contentArea.x - m_scrollX;
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 6. Draw the selection highlight FIRST (under the text). This
|
||||
// way the text stays the regular color and just sits on top
|
||||
// of a colored band — like every text editor.
|
||||
// -------------------------------------------------------------
|
||||
if (m_buffer.hasSelection())
|
||||
{
|
||||
const u32 selStart = m_buffer.selectionStart();
|
||||
const u32 selEnd = m_buffer.selectionEnd();
|
||||
const f32 x1 = textX + layout_x_for_byte(selStart);
|
||||
const f32 x2 = textX + layout_x_for_byte(selEnd);
|
||||
gfx.fillRect(
|
||||
{ x1, textY, x2 - x1, m_layout.lineHeight },
|
||||
m_selectionColor
|
||||
);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 7. Draw the text itself. Single drawString call — the layout
|
||||
// cache is only used for cursor / selection positioning;
|
||||
// actual glyph rendering goes through the renderer's existing
|
||||
// code path (which handles kerning, atlas, etc.).
|
||||
// -------------------------------------------------------------
|
||||
if (!m_buffer.empty())
|
||||
gfx.drawString(m_buffer.text(), { textX, textY }, getTextColor(), getFontSize());
|
||||
|
||||
// -------------------------------------------------------------
|
||||
// 8. Draw the cursor. Only when focused and the blink is in the
|
||||
// "on" phase. The cursor renders ON TOP of everything else so
|
||||
// it's visible inside selections.
|
||||
// -------------------------------------------------------------
|
||||
if (isFocused() && (m_cursorState || !m_cursorBlink))
|
||||
{
|
||||
const f32 cx = textX + layout_x_for_byte(m_buffer.cursorByteOffset());
|
||||
gfx.fillRect(
|
||||
{ cx, textY, m_cursorWidth, m_layout.lineHeight },
|
||||
m_cursorColor
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::onUpdate(void)
|
||||
{
|
||||
m_keyRepeatTimer.update();
|
||||
m_doubleClickTimer.update();
|
||||
if (m_cursorBlink)
|
||||
{
|
||||
m_cursorBlinkTimer.update();
|
||||
if (m_cursorBlinkTimer.isDone())
|
||||
{
|
||||
m_cursorState = !m_cursorState;
|
||||
m_cursorBlinkTimer.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-scroll while drag-selecting past the widget edges.
|
||||
if (m_mouseSelecting)
|
||||
{
|
||||
const auto bounds = getGlobalBounds();
|
||||
const f32 contentLeft = m_textPadding.left;
|
||||
const f32 contentRight = bounds.w - m_textPadding.right;
|
||||
|
||||
// Only act when mouse is outside the content area horizontally.
|
||||
if (m_lastMouseLocalX < contentLeft || m_lastMouseLocalX > contentRight)
|
||||
{
|
||||
// Use the same hit-test as drag, but it'll naturally clamp to
|
||||
// start/end of buffer when the mouse is way outside. The
|
||||
// setCursorByteOffset call will trigger ensure_cursor_visible
|
||||
// on the next draw, which scrolls the view to follow.
|
||||
const u32 hit = byte_offset_for_pixel_x(m_lastMouseLocalX);
|
||||
m_buffer.setCursorByteOffset(hit, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::onFocusGained(const Event& event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextEdit::onFocusLost(const Event& event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextEdit::onTextEntered(const Event& event)
|
||||
{
|
||||
auto& data = *event.keyboard;
|
||||
if (!m_charFilter || m_charFilter->isValidChar(data.text))
|
||||
{
|
||||
if (m_lastChar == data.text && m_keyRepeatTimer.isCounting())
|
||||
return;
|
||||
m_buffer.insertText(data.text);
|
||||
m_lastChar = data.text;
|
||||
m_keyRepeatTimer.start();
|
||||
if (m_cursorBlink)
|
||||
m_cursorBlinkTimer.start();
|
||||
m_cursorState = true;
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::onKeyPressed(const Event& event)
|
||||
{
|
||||
auto& data = *event.keyboard;
|
||||
if (m_lastKeyCode == data.keyCode && m_keyRepeatTimer.isCounting())
|
||||
return;
|
||||
if (data.keyCode == KeyCode::Backspace)
|
||||
{
|
||||
m_keyRepeatTimer.start();
|
||||
m_lastKeyCode = data.keyCode;
|
||||
m_buffer.backspace();
|
||||
}
|
||||
else if (data.keyCode == KeyCode::Left)
|
||||
{
|
||||
m_keyRepeatTimer.start();
|
||||
m_lastKeyCode = data.keyCode;
|
||||
m_buffer.moveLeft();
|
||||
}
|
||||
else if (data.keyCode == KeyCode::Right)
|
||||
{
|
||||
m_keyRepeatTimer.start();
|
||||
|
||||
m_lastKeyCode = data.keyCode;
|
||||
m_buffer.moveRight();
|
||||
}
|
||||
else if (data.keyCode == KeyCode::Return)
|
||||
{
|
||||
m_keyRepeatTimer.start();
|
||||
m_lastKeyCode = data.keyCode;
|
||||
if (callback_onActionPerformed)
|
||||
callback_onActionPerformed(event);
|
||||
}
|
||||
}
|
||||
|
||||
void TextEdit::onKeyReleased(const Event& event)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void TextEdit::onMousePressed(const Event& event)
|
||||
{
|
||||
auto& data = *event.mouse;
|
||||
const f32 local_x = data.position_x - getGlobalBounds().x;
|
||||
const u32 hit = byte_offset_for_pixel_x(local_x);
|
||||
|
||||
// Double-click detection: a second press within the timer window AND
|
||||
// close enough to the last press position counts as a double-click.
|
||||
// The position check matters — without it, two presses far apart in
|
||||
// a long document would falsely trigger word selection.
|
||||
constexpr f32 doubleClickRadius = 5.0f;
|
||||
const bool isDoubleClick =
|
||||
m_lastClickValid
|
||||
&& m_doubleClickTimer.isCounting()
|
||||
&& std::abs(local_x - m_lastClickLocalX) < doubleClickRadius;
|
||||
|
||||
if (isDoubleClick)
|
||||
{
|
||||
// Word selection. selectWordAt sets both anchor and cursor;
|
||||
// we mark the drag flag so subsequent drag motion extends the
|
||||
// selection by word — though we'll keep the simple implementation
|
||||
// for now where dragging after a double-click extends by character.
|
||||
m_buffer.selectWordAt(hit);
|
||||
m_mouseSelecting = true;
|
||||
m_lastClickValid = false; // Don't triple-click into something weird.
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buffer.setCursorByteOffset(hit);
|
||||
m_mouseSelecting = true;
|
||||
m_lastClickLocalX = local_x;
|
||||
m_lastMouseLocalX = local_x;
|
||||
m_lastClickValid = true;
|
||||
m_doubleClickTimer.start();
|
||||
}
|
||||
|
||||
if (m_cursorBlink) m_cursorBlinkTimer.start();
|
||||
m_cursorState = true;
|
||||
}
|
||||
|
||||
void TextEdit::onMouseReleased(const Event& event)
|
||||
{
|
||||
m_mouseSelecting = false;
|
||||
}
|
||||
|
||||
void TextEdit::onMouseDragged(const Event& event)
|
||||
{
|
||||
if (!m_mouseSelecting) return;
|
||||
auto& data = *event.mouse;
|
||||
const f32 local_x = data.position_x - getGlobalBounds().x;
|
||||
const u32 hit = byte_offset_for_pixel_x(local_x);
|
||||
|
||||
// Extend selection to the dragged-to position. The anchor was set by
|
||||
// setCursorByteOffset(hit) in onMousePressed and stays put.
|
||||
m_buffer.setCursorByteOffset(hit, true);
|
||||
m_lastMouseLocalX = local_x;
|
||||
|
||||
if (m_cursorBlink) m_cursorBlinkTimer.start();
|
||||
m_cursorState = true;
|
||||
}
|
||||
|
||||
void TextEdit::rebuild_layout(BasicRenderer2D& gfx)
|
||||
{
|
||||
m_layout.byteOffsets.clear();
|
||||
m_layout.xPositions.clear();
|
||||
m_layout.totalWidth = 0.0f;
|
||||
|
||||
// Empty buffer: a single entry for "x=0 at byte 0" so the cursor
|
||||
// still has a position to render at.
|
||||
if (m_buffer.empty())
|
||||
{
|
||||
m_layout.lineHeight = gfx.getStringDimensions(" ", getFontSize()).y;
|
||||
m_layout.byteOffsets.push_back(0);
|
||||
m_layout.xPositions.push_back(0.0f);
|
||||
m_layout.dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// One call to the renderer. Returns one Vec2 per codepoint:
|
||||
// .x = advance (incl. kerning) contributed by that glyph
|
||||
// .y = glyph height (same for all glyphs at this font/size)
|
||||
const stdvec<Vec2> perChar =
|
||||
gfx.getStringDimensionsPerCharacter(m_buffer.text(), getFontSize());
|
||||
|
||||
if (perChar.empty())
|
||||
{
|
||||
// Defensive: shouldn't happen with non-empty text, but if the
|
||||
// font failed to load we still want a valid layout.
|
||||
m_layout.lineHeight = 0.0f;
|
||||
m_layout.byteOffsets.push_back(0);
|
||||
m_layout.xPositions.push_back(0.0f);
|
||||
m_layout.dirty = false;
|
||||
return;
|
||||
}
|
||||
|
||||
m_layout.lineHeight = perChar[0].y;
|
||||
|
||||
// Walk both the per-character advances AND the buffer's UTF-8 bytes
|
||||
// in lockstep. Since processString emits exactly one glyph per
|
||||
// codepoint, the i-th advance corresponds to the codepoint that
|
||||
// starts at the i-th byte boundary.
|
||||
const ostd::cpp_string& s = m_buffer.text().cpp_str();
|
||||
f32 x = 0.0f;
|
||||
u32 byte_pos = 0;
|
||||
size_t glyph_idx = 0;
|
||||
|
||||
// First entry: position before any glyph.
|
||||
m_layout.byteOffsets.push_back(0);
|
||||
m_layout.xPositions.push_back(0.0f);
|
||||
|
||||
while (byte_pos < s.size() && glyph_idx < perChar.size())
|
||||
{
|
||||
x += perChar[glyph_idx].x;
|
||||
byte_pos = String::utf8::next_codepoint_start(s, byte_pos);
|
||||
m_layout.byteOffsets.push_back(byte_pos);
|
||||
m_layout.xPositions.push_back(x);
|
||||
glyph_idx++;
|
||||
}
|
||||
|
||||
m_layout.totalWidth = x;
|
||||
m_layout.dirty = false;
|
||||
}
|
||||
|
||||
f32 TextEdit::layout_x_for_byte(u32 byte_offset) const
|
||||
{
|
||||
// Binary search for the entry. Layout invariant: byteOffsets is
|
||||
// strictly increasing, so std::lower_bound gives us the right index.
|
||||
const auto& v = m_layout.byteOffsets;
|
||||
if (v.empty()) return 0.0f;
|
||||
if (byte_offset >= v.back()) return m_layout.xPositions.back();
|
||||
|
||||
auto it = std::lower_bound(v.begin(), v.end(), byte_offset);
|
||||
const size_t idx = std::distance(v.begin(), it);
|
||||
return m_layout.xPositions[idx];
|
||||
}
|
||||
|
||||
void TextEdit::ensure_cursor_visible(const Rectangle& bounds)
|
||||
{
|
||||
const f32 contentW = bounds.w - m_textPadding.left - m_textPadding.right;
|
||||
if (contentW <= 0.0f) return;
|
||||
|
||||
const f32 cursorX = layout_x_for_byte(m_buffer.cursorByteOffset());
|
||||
const f32 totalW = m_layout.totalWidth;
|
||||
|
||||
// Margin: how far the cursor should stay from the visible edges.
|
||||
// ~2 character widths feels right; using line height as a proxy works
|
||||
// well across font sizes since char width scales similarly.
|
||||
// This single-sourced value also caps itself at 25% of contentW so it
|
||||
// stays sensible even in very narrow widgets.
|
||||
const f32 marginRaw = m_layout.lineHeight * 0.6f;
|
||||
const f32 margin = std::min(marginRaw, contentW * 0.25f);
|
||||
|
||||
// Case 1: all the text fits. No scroll needed.
|
||||
if (totalW <= contentW)
|
||||
{
|
||||
m_scrollX = 0.0f;
|
||||
return;
|
||||
}
|
||||
|
||||
// Case 2: text overflows. Apply margin-aware scrolling.
|
||||
//
|
||||
// The visible window in document space is [m_scrollX, m_scrollX + contentW).
|
||||
// We want cursorX to satisfy:
|
||||
// m_scrollX + margin <= cursorX <= m_scrollX + contentW - margin
|
||||
// — except clamped at the document edges.
|
||||
|
||||
// If cursor is too close to the left edge of the visible window, scroll
|
||||
// left so there's `margin` of text visible to its left.
|
||||
if (cursorX < m_scrollX + margin)
|
||||
m_scrollX = cursorX - margin;
|
||||
|
||||
// If cursor is too close to the right edge, scroll right so there's
|
||||
// `margin` of room to its right.
|
||||
else if (cursorX > m_scrollX + contentW - margin)
|
||||
m_scrollX = cursorX - contentW + margin;
|
||||
|
||||
// Clamp scrollX to valid range. The lower bound is 0 (don't show empty
|
||||
// space to the left of the text). The upper bound is totalW - contentW
|
||||
// (don't show empty space to the right of the text — i.e., the right
|
||||
// edge of the text aligns with the right edge of the visible area).
|
||||
//
|
||||
// This upper clamp is what produces the "right-anchored when at end"
|
||||
// behavior automatically: when cursor is at end, the margin formula
|
||||
// wants to scroll past the end of the text, but we clamp it back so
|
||||
// the text's right edge sits at the visible area's right edge.
|
||||
if (m_scrollX < 0.0f)
|
||||
m_scrollX = 0.0f;
|
||||
if (m_scrollX > totalW - contentW)
|
||||
m_scrollX = totalW - contentW;
|
||||
}
|
||||
|
||||
u32 TextEdit::byte_offset_for_pixel_x(f32 widget_local_x) const
|
||||
{
|
||||
const f32 doc_x = widget_local_x - m_textPadding.left + m_scrollX;
|
||||
|
||||
const auto& xs = m_layout.xPositions;
|
||||
const auto& bs = m_layout.byteOffsets;
|
||||
if (xs.empty()) return 0;
|
||||
if (doc_x <= xs.front()) return bs.front();
|
||||
if (doc_x >= xs.back()) return bs.back();
|
||||
|
||||
auto it = std::upper_bound(xs.begin(), xs.end(), doc_x);
|
||||
const size_t hi = std::distance(xs.begin(), it);
|
||||
const size_t lo = hi - 1;
|
||||
const f32 distLo = doc_x - xs[lo];
|
||||
const f32 distHi = xs[hi] - doc_x;
|
||||
return cast<u32>((distLo <= distHi) ? bs[lo] : bs[hi]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,11 +20,77 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Widget.hpp"
|
||||
#include <ogfx/gui/widgets/Widget.hpp>
|
||||
#include <ostd/string/TextBuffer.hpp>
|
||||
#include <ostd/utils/Time.hpp>
|
||||
|
||||
namespace ostd
|
||||
namespace ogfx
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
class TextEdit : public Widget
|
||||
{
|
||||
public: class CharacterFilter
|
||||
{
|
||||
public:
|
||||
inline virtual bool isValidChar(const String& utf8) { return true; }
|
||||
};
|
||||
public: struct TextPadding
|
||||
{
|
||||
f32 left { 6.0f };
|
||||
f32 right { 6.0f };
|
||||
};
|
||||
public: struct GlyphLayout
|
||||
{
|
||||
stdvec<u32> byteOffsets; // size N+1: byte offset before each codepoint, plus end
|
||||
stdvec<f32> xPositions; // size N+1: pixel x before each codepoint, plus end-x
|
||||
f32 totalWidth { 0 };
|
||||
f32 lineHeight { 0 };
|
||||
bool dirty { true };
|
||||
};
|
||||
public:
|
||||
inline TextEdit(Window& window) : Widget({ 0, 0, 0, 0 }, window) { create(); }
|
||||
TextEdit& create(void);
|
||||
void applyTheme(const ostd::Stylesheet& theme) override;
|
||||
void onDraw(ogfx::BasicRenderer2D& gfx) override;
|
||||
void onUpdate(void) override;
|
||||
void onFocusGained(const Event& event) override;
|
||||
void onFocusLost(const Event& event) override;
|
||||
void onTextEntered(const Event& event) override;
|
||||
void onKeyPressed(const Event& event) override;
|
||||
void onKeyReleased(const Event& event) override;
|
||||
void onMousePressed(const Event& event) override;
|
||||
void onMouseReleased(const Event& event) override;
|
||||
void onMouseDragged(const Event& event) override;
|
||||
|
||||
inline void setText(const String& text) { m_buffer.setText(text); }
|
||||
|
||||
private:
|
||||
void rebuild_layout(BasicRenderer2D& gfx);
|
||||
void ensure_cursor_visible(const Rectangle& bounds);
|
||||
f32 layout_x_for_byte(u32 byte_offset) const;
|
||||
u32 byte_offset_for_pixel_x(f32 widget_local_x) const;
|
||||
|
||||
private:
|
||||
ostd::TextBuffer m_buffer { "", true };
|
||||
ostd::BasicCounter m_cursorBlinkTimer { 30 };
|
||||
ostd::BasicCounter m_keyRepeatTimer { 1 };
|
||||
ostd::BasicCounter m_doubleClickTimer { 24 }; // ~400ms at 60fps
|
||||
f32 m_lastClickLocalX { 0.0f };
|
||||
bool m_lastClickValid { false };
|
||||
bool m_mouseSelecting { false };
|
||||
f32 m_lastMouseLocalX { 0.0f };
|
||||
CharacterFilter* m_charFilter { nullptr };
|
||||
String m_lastChar { "" };
|
||||
i32 m_lastKeyCode { 0 };
|
||||
bool m_cursorBlink { true };
|
||||
bool m_cursorState { false };
|
||||
f32 m_cursorWidth { 2 };
|
||||
Color m_cursorColor { Colors::Red };
|
||||
GlyphLayout m_layout;
|
||||
f32 m_scrollX { 0.0f };
|
||||
TextPadding m_textPadding;
|
||||
Color m_selectionColor { 60, 110, 200, 200 }; // bluish, semi-transparent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
|
||||
This file is part of OmniaFramework.
|
||||
This file is part of OmniaFramework.
|
||||
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
|
@ -26,261 +26,261 @@ namespace ogfx
|
|||
{
|
||||
struct KeyCode
|
||||
{
|
||||
inline static constexpr u32 Unknown { SDLK_UNKNOWN };
|
||||
inline static constexpr u32 Return { SDLK_RETURN };
|
||||
inline static constexpr u32 Escape { SDLK_ESCAPE };
|
||||
inline static constexpr u32 Backspace { SDLK_BACKSPACE };
|
||||
inline static constexpr u32 Tab { SDLK_TAB };
|
||||
inline static constexpr u32 Space { SDLK_SPACE };
|
||||
inline static constexpr u32 Exclaim { SDLK_EXCLAIM };
|
||||
inline static constexpr u32 Dblapostrophe { SDLK_DBLAPOSTROPHE };
|
||||
inline static constexpr u32 Hash { SDLK_HASH };
|
||||
inline static constexpr u32 Dollar { SDLK_DOLLAR };
|
||||
inline static constexpr u32 Percent { SDLK_PERCENT };
|
||||
inline static constexpr u32 Ampersand { SDLK_AMPERSAND };
|
||||
inline static constexpr u32 Apostrophe { SDLK_APOSTROPHE };
|
||||
inline static constexpr u32 Leftparen { SDLK_LEFTPAREN };
|
||||
inline static constexpr u32 Rightparen { SDLK_RIGHTPAREN };
|
||||
inline static constexpr u32 Asterisk { SDLK_ASTERISK };
|
||||
inline static constexpr u32 Plus { SDLK_PLUS };
|
||||
inline static constexpr u32 Comma { SDLK_COMMA };
|
||||
inline static constexpr u32 Minus { SDLK_MINUS };
|
||||
inline static constexpr u32 Period { SDLK_PERIOD };
|
||||
inline static constexpr u32 Slash { SDLK_SLASH };
|
||||
inline static constexpr u32 Num0 { SDLK_0 };
|
||||
inline static constexpr u32 Num1 { SDLK_1 };
|
||||
inline static constexpr u32 Num2 { SDLK_2 };
|
||||
inline static constexpr u32 Num3 { SDLK_3 };
|
||||
inline static constexpr u32 Num4 { SDLK_4 };
|
||||
inline static constexpr u32 Num5 { SDLK_5 };
|
||||
inline static constexpr u32 Num6 { SDLK_6 };
|
||||
inline static constexpr u32 Num7 { SDLK_7 };
|
||||
inline static constexpr u32 Num8 { SDLK_8 };
|
||||
inline static constexpr u32 Num9 { SDLK_9 };
|
||||
inline static constexpr u32 Colon { SDLK_COLON };
|
||||
inline static constexpr u32 Semicolon { SDLK_SEMICOLON };
|
||||
inline static constexpr u32 Less { SDLK_LESS };
|
||||
inline static constexpr u32 Equals { SDLK_EQUALS };
|
||||
inline static constexpr u32 Greater { SDLK_GREATER };
|
||||
inline static constexpr u32 Question { SDLK_QUESTION };
|
||||
inline static constexpr u32 At { SDLK_AT };
|
||||
inline static constexpr u32 LeftBracket { SDLK_LEFTBRACKET };
|
||||
inline static constexpr u32 Backslash { SDLK_BACKSLASH };
|
||||
inline static constexpr u32 RightBracket { SDLK_RIGHTBRACKET };
|
||||
inline static constexpr u32 Caret { SDLK_CARET };
|
||||
inline static constexpr u32 Underscore { SDLK_UNDERSCORE };
|
||||
inline static constexpr u32 Grave { SDLK_GRAVE };
|
||||
inline static constexpr u32 A { SDLK_A };
|
||||
inline static constexpr u32 B { SDLK_B };
|
||||
inline static constexpr u32 C { SDLK_C };
|
||||
inline static constexpr u32 D { SDLK_D };
|
||||
inline static constexpr u32 E { SDLK_E };
|
||||
inline static constexpr u32 F { SDLK_F };
|
||||
inline static constexpr u32 G { SDLK_G };
|
||||
inline static constexpr u32 H { SDLK_H };
|
||||
inline static constexpr u32 I { SDLK_I };
|
||||
inline static constexpr u32 J { SDLK_J };
|
||||
inline static constexpr u32 K { SDLK_K };
|
||||
inline static constexpr u32 L { SDLK_L };
|
||||
inline static constexpr u32 M { SDLK_M };
|
||||
inline static constexpr u32 N { SDLK_N };
|
||||
inline static constexpr u32 O { SDLK_O };
|
||||
inline static constexpr u32 P { SDLK_P };
|
||||
inline static constexpr u32 Q { SDLK_Q };
|
||||
inline static constexpr u32 R { SDLK_R };
|
||||
inline static constexpr u32 S { SDLK_S };
|
||||
inline static constexpr u32 T { SDLK_T };
|
||||
inline static constexpr u32 U { SDLK_U };
|
||||
inline static constexpr u32 V { SDLK_V };
|
||||
inline static constexpr u32 W { SDLK_W };
|
||||
inline static constexpr u32 X { SDLK_X };
|
||||
inline static constexpr u32 Y { SDLK_Y };
|
||||
inline static constexpr u32 Z { SDLK_Z };
|
||||
inline static constexpr u32 Leftbrace { SDLK_LEFTBRACE };
|
||||
inline static constexpr u32 Pipe { SDLK_PIPE };
|
||||
inline static constexpr u32 Rightbrace { SDLK_RIGHTBRACE };
|
||||
inline static constexpr u32 Tilde { SDLK_TILDE };
|
||||
inline static constexpr u32 Delete { SDLK_DELETE };
|
||||
inline static constexpr u32 Plusminus { SDLK_PLUSMINUS };
|
||||
inline static constexpr u32 Capslock { SDLK_CAPSLOCK };
|
||||
inline static constexpr u32 F1 { SDLK_F1 };
|
||||
inline static constexpr u32 F2 { SDLK_F2 };
|
||||
inline static constexpr u32 F3 { SDLK_F3 };
|
||||
inline static constexpr u32 F4 { SDLK_F4 };
|
||||
inline static constexpr u32 F5 { SDLK_F5 };
|
||||
inline static constexpr u32 F6 { SDLK_F6 };
|
||||
inline static constexpr u32 F7 { SDLK_F7 };
|
||||
inline static constexpr u32 F8 { SDLK_F8 };
|
||||
inline static constexpr u32 F9 { SDLK_F9 };
|
||||
inline static constexpr u32 F10 { SDLK_F10 };
|
||||
inline static constexpr u32 F11 { SDLK_F11 };
|
||||
inline static constexpr u32 F12 { SDLK_F12 };
|
||||
inline static constexpr u32 Printscreen { SDLK_PRINTSCREEN };
|
||||
inline static constexpr u32 Scrolllock { SDLK_SCROLLLOCK };
|
||||
inline static constexpr u32 Pause { SDLK_PAUSE };
|
||||
inline static constexpr u32 Insert { SDLK_INSERT };
|
||||
inline static constexpr u32 Home { SDLK_HOME };
|
||||
inline static constexpr u32 Pageup { SDLK_PAGEUP };
|
||||
inline static constexpr u32 End { SDLK_END };
|
||||
inline static constexpr u32 Pagedown { SDLK_PAGEDOWN };
|
||||
inline static constexpr u32 Right { SDLK_RIGHT };
|
||||
inline static constexpr u32 Left { SDLK_LEFT };
|
||||
inline static constexpr u32 Down { SDLK_DOWN };
|
||||
inline static constexpr u32 Up { SDLK_UP };
|
||||
inline static constexpr u32 Numlockclear { SDLK_NUMLOCKCLEAR };
|
||||
inline static constexpr u32 KpDivide { SDLK_KP_DIVIDE };
|
||||
inline static constexpr u32 KpMultiply { SDLK_KP_MULTIPLY };
|
||||
inline static constexpr u32 KpMinus { SDLK_KP_MINUS };
|
||||
inline static constexpr u32 KpPlus { SDLK_KP_PLUS };
|
||||
inline static constexpr u32 KpEnter { SDLK_KP_ENTER };
|
||||
inline static constexpr u32 Kp1 { SDLK_KP_1 };
|
||||
inline static constexpr u32 Kp2 { SDLK_KP_2 };
|
||||
inline static constexpr u32 Kp3 { SDLK_KP_3 };
|
||||
inline static constexpr u32 Kp4 { SDLK_KP_4 };
|
||||
inline static constexpr u32 Kp5 { SDLK_KP_5 };
|
||||
inline static constexpr u32 Kp6 { SDLK_KP_6 };
|
||||
inline static constexpr u32 Kp7 { SDLK_KP_7 };
|
||||
inline static constexpr u32 Kp8 { SDLK_KP_8 };
|
||||
inline static constexpr u32 Kp9 { SDLK_KP_9 };
|
||||
inline static constexpr u32 Kp0 { SDLK_KP_0 };
|
||||
inline static constexpr u32 KpPeriod { SDLK_KP_PERIOD };
|
||||
inline static constexpr u32 Application { SDLK_APPLICATION };
|
||||
inline static constexpr u32 Power { SDLK_POWER };
|
||||
inline static constexpr u32 KpEquals { SDLK_KP_EQUALS };
|
||||
inline static constexpr u32 F13 { SDLK_F13 };
|
||||
inline static constexpr u32 F14 { SDLK_F14 };
|
||||
inline static constexpr u32 F15 { SDLK_F15 };
|
||||
inline static constexpr u32 F16 { SDLK_F16 };
|
||||
inline static constexpr u32 F17 { SDLK_F17 };
|
||||
inline static constexpr u32 F18 { SDLK_F18 };
|
||||
inline static constexpr u32 F19 { SDLK_F19 };
|
||||
inline static constexpr u32 F20 { SDLK_F20 };
|
||||
inline static constexpr u32 F21 { SDLK_F21 };
|
||||
inline static constexpr u32 F22 { SDLK_F22 };
|
||||
inline static constexpr u32 F23 { SDLK_F23 };
|
||||
inline static constexpr u32 F24 { SDLK_F24 };
|
||||
inline static constexpr u32 Execute { SDLK_EXECUTE };
|
||||
inline static constexpr u32 Help { SDLK_HELP };
|
||||
inline static constexpr u32 Menu { SDLK_MENU };
|
||||
inline static constexpr u32 Select { SDLK_SELECT };
|
||||
inline static constexpr u32 Stop { SDLK_STOP };
|
||||
inline static constexpr u32 Again { SDLK_AGAIN };
|
||||
inline static constexpr u32 Undo { SDLK_UNDO };
|
||||
inline static constexpr u32 Cut { SDLK_CUT };
|
||||
inline static constexpr u32 Copy { SDLK_COPY };
|
||||
inline static constexpr u32 Paste { SDLK_PASTE };
|
||||
inline static constexpr u32 Find { SDLK_FIND };
|
||||
inline static constexpr u32 Mute { SDLK_MUTE };
|
||||
inline static constexpr u32 Volumeup { SDLK_VOLUMEUP };
|
||||
inline static constexpr u32 Volumedown { SDLK_VOLUMEDOWN };
|
||||
inline static constexpr u32 KpComma { SDLK_KP_COMMA };
|
||||
inline static constexpr u32 KpEqualsas400 { SDLK_KP_EQUALSAS400 };
|
||||
inline static constexpr u32 Alterase { SDLK_ALTERASE };
|
||||
inline static constexpr u32 Sysreq { SDLK_SYSREQ };
|
||||
inline static constexpr u32 Cancel { SDLK_CANCEL };
|
||||
inline static constexpr u32 Clear { SDLK_CLEAR };
|
||||
inline static constexpr u32 Prior { SDLK_PRIOR };
|
||||
inline static constexpr u32 Return2 { SDLK_RETURN2 };
|
||||
inline static constexpr u32 Separator { SDLK_SEPARATOR };
|
||||
inline static constexpr u32 Out { SDLK_OUT };
|
||||
inline static constexpr u32 Oper { SDLK_OPER };
|
||||
inline static constexpr u32 Clearagain { SDLK_CLEARAGAIN };
|
||||
inline static constexpr u32 Crsel { SDLK_CRSEL };
|
||||
inline static constexpr u32 Exsel { SDLK_EXSEL };
|
||||
inline static constexpr u32 Kp00 { SDLK_KP_00 };
|
||||
inline static constexpr u32 Kp000 { SDLK_KP_000 };
|
||||
inline static constexpr u32 Thousandsseparator { SDLK_THOUSANDSSEPARATOR };
|
||||
inline static constexpr u32 Decimalseparator { SDLK_DECIMALSEPARATOR };
|
||||
inline static constexpr u32 Currencyunit { SDLK_CURRENCYUNIT };
|
||||
inline static constexpr u32 Currencysubunit { SDLK_CURRENCYSUBUNIT };
|
||||
inline static constexpr u32 KpLeftparen { SDLK_KP_LEFTPAREN };
|
||||
inline static constexpr u32 KpRightparen { SDLK_KP_RIGHTPAREN };
|
||||
inline static constexpr u32 KpLeftbrace { SDLK_KP_LEFTBRACE };
|
||||
inline static constexpr u32 KpRightbrace { SDLK_KP_RIGHTBRACE };
|
||||
inline static constexpr u32 KpTab { SDLK_KP_TAB };
|
||||
inline static constexpr u32 KpBackspace { SDLK_KP_BACKSPACE };
|
||||
inline static constexpr u32 KpA { SDLK_KP_A };
|
||||
inline static constexpr u32 KpB { SDLK_KP_B };
|
||||
inline static constexpr u32 KpC { SDLK_KP_C };
|
||||
inline static constexpr u32 KpD { SDLK_KP_D };
|
||||
inline static constexpr u32 KpE { SDLK_KP_E };
|
||||
inline static constexpr u32 KpF { SDLK_KP_F };
|
||||
inline static constexpr u32 KpXor { SDLK_KP_XOR };
|
||||
inline static constexpr u32 KpPower { SDLK_KP_POWER };
|
||||
inline static constexpr u32 KpPercent { SDLK_KP_PERCENT };
|
||||
inline static constexpr u32 KpLess { SDLK_KP_LESS };
|
||||
inline static constexpr u32 KpGreater { SDLK_KP_GREATER };
|
||||
inline static constexpr u32 KpAmpersand { SDLK_KP_AMPERSAND };
|
||||
inline static constexpr u32 KpDblampersand { SDLK_KP_DBLAMPERSAND };
|
||||
inline static constexpr u32 KpVerticalbar { SDLK_KP_VERTICALBAR };
|
||||
inline static constexpr u32 KpDblverticalbar { SDLK_KP_DBLVERTICALBAR };
|
||||
inline static constexpr u32 KpColon { SDLK_KP_COLON };
|
||||
inline static constexpr u32 KpHash { SDLK_KP_HASH };
|
||||
inline static constexpr u32 KpSpace { SDLK_KP_SPACE };
|
||||
inline static constexpr u32 KpAt { SDLK_KP_AT };
|
||||
inline static constexpr u32 KpExclam { SDLK_KP_EXCLAM };
|
||||
inline static constexpr u32 KpMemstore { SDLK_KP_MEMSTORE };
|
||||
inline static constexpr u32 KpMemrecall { SDLK_KP_MEMRECALL };
|
||||
inline static constexpr u32 KpMemclear { SDLK_KP_MEMCLEAR };
|
||||
inline static constexpr u32 KpMemadd { SDLK_KP_MEMADD };
|
||||
inline static constexpr u32 KpMemsubtract { SDLK_KP_MEMSUBTRACT };
|
||||
inline static constexpr u32 KpMemmultiply { SDLK_KP_MEMMULTIPLY };
|
||||
inline static constexpr u32 KpMemdivide { SDLK_KP_MEMDIVIDE };
|
||||
inline static constexpr u32 KpPlusminus { SDLK_KP_PLUSMINUS };
|
||||
inline static constexpr u32 KpClear { SDLK_KP_CLEAR };
|
||||
inline static constexpr u32 KpClearentry { SDLK_KP_CLEARENTRY };
|
||||
inline static constexpr u32 KpBinary { SDLK_KP_BINARY };
|
||||
inline static constexpr u32 KpOctal { SDLK_KP_OCTAL };
|
||||
inline static constexpr u32 KpDecimal { SDLK_KP_DECIMAL };
|
||||
inline static constexpr u32 KpHexadecimal { SDLK_KP_HEXADECIMAL };
|
||||
inline static constexpr u32 Lctrl { SDLK_LCTRL };
|
||||
inline static constexpr u32 Lshift { SDLK_LSHIFT };
|
||||
inline static constexpr u32 Lalt { SDLK_LALT };
|
||||
inline static constexpr u32 Lgui { SDLK_LGUI };
|
||||
inline static constexpr u32 Rctrl { SDLK_RCTRL };
|
||||
inline static constexpr u32 Rshift { SDLK_RSHIFT };
|
||||
inline static constexpr u32 Ralt { SDLK_RALT };
|
||||
inline static constexpr u32 Rgui { SDLK_RGUI };
|
||||
inline static constexpr u32 Mode { SDLK_MODE };
|
||||
inline static constexpr u32 Sleep { SDLK_SLEEP };
|
||||
inline static constexpr u32 Wake { SDLK_WAKE };
|
||||
inline static constexpr u32 ChannelIncrement { SDLK_CHANNEL_INCREMENT };
|
||||
inline static constexpr u32 ChannelDecrement { SDLK_CHANNEL_DECREMENT };
|
||||
inline static constexpr u32 MediaPlay { SDLK_MEDIA_PLAY };
|
||||
inline static constexpr u32 MediaPause { SDLK_MEDIA_PAUSE };
|
||||
inline static constexpr u32 MediaRecord { SDLK_MEDIA_RECORD };
|
||||
inline static constexpr u32 MediaFastForward { SDLK_MEDIA_FAST_FORWARD };
|
||||
inline static constexpr u32 MediaRewind { SDLK_MEDIA_REWIND };
|
||||
inline static constexpr u32 MediaNextTrack { SDLK_MEDIA_NEXT_TRACK };
|
||||
inline static constexpr u32 MediaPreviousTrack { SDLK_MEDIA_PREVIOUS_TRACK };
|
||||
inline static constexpr u32 MediaStop { SDLK_MEDIA_STOP };
|
||||
inline static constexpr u32 MediaEject { SDLK_MEDIA_EJECT };
|
||||
inline static constexpr u32 MediaPlayPause { SDLK_MEDIA_PLAY_PAUSE };
|
||||
inline static constexpr u32 MediaSelect { SDLK_MEDIA_SELECT };
|
||||
inline static constexpr u32 AcNew { SDLK_AC_NEW };
|
||||
inline static constexpr u32 AcOpen { SDLK_AC_OPEN };
|
||||
inline static constexpr u32 AcClose { SDLK_AC_CLOSE };
|
||||
inline static constexpr u32 AcExit { SDLK_AC_EXIT };
|
||||
inline static constexpr u32 AcSave { SDLK_AC_SAVE };
|
||||
inline static constexpr u32 AcPrint { SDLK_AC_PRINT };
|
||||
inline static constexpr u32 AcProperties { SDLK_AC_PROPERTIES };
|
||||
inline static constexpr u32 AcSearch { SDLK_AC_SEARCH };
|
||||
inline static constexpr u32 AcHome { SDLK_AC_HOME };
|
||||
inline static constexpr u32 AcBack { SDLK_AC_BACK };
|
||||
inline static constexpr u32 AcForward { SDLK_AC_FORWARD };
|
||||
inline static constexpr u32 AcStop { SDLK_AC_STOP };
|
||||
inline static constexpr u32 AcRefresh { SDLK_AC_REFRESH };
|
||||
inline static constexpr u32 AcBookmarks { SDLK_AC_BOOKMARKS };
|
||||
inline static constexpr u32 Softleft { SDLK_SOFTLEFT };
|
||||
inline static constexpr u32 Softright { SDLK_SOFTRIGHT };
|
||||
inline static constexpr u32 Call { SDLK_CALL };
|
||||
inline static constexpr u32 Endcall { SDLK_ENDCALL };
|
||||
inline static constexpr u32 LeftTab { SDLK_LEFT_TAB };
|
||||
inline static constexpr u32 Level5Shift { SDLK_LEVEL5_SHIFT };
|
||||
inline static constexpr u32 MultiKeyCompose { SDLK_MULTI_KEY_COMPOSE };
|
||||
inline static constexpr u32 Lmeta { SDLK_LMETA };
|
||||
inline static constexpr u32 Rmeta { SDLK_RMETA };
|
||||
inline static constexpr u32 Lhyper { SDLK_LHYPER };
|
||||
inline static constexpr u32 Rhyper { SDLK_RHYPER };
|
||||
inline static constexpr u32 Unknown { SDLK_UNKNOWN };
|
||||
inline static constexpr u32 Return { SDLK_RETURN };
|
||||
inline static constexpr u32 Escape { SDLK_ESCAPE };
|
||||
inline static constexpr u32 Backspace { SDLK_BACKSPACE };
|
||||
inline static constexpr u32 Tab { SDLK_TAB };
|
||||
inline static constexpr u32 Space { SDLK_SPACE };
|
||||
inline static constexpr u32 Exclaim { SDLK_EXCLAIM };
|
||||
inline static constexpr u32 Dblapostrophe { SDLK_DBLAPOSTROPHE };
|
||||
inline static constexpr u32 Hash { SDLK_HASH };
|
||||
inline static constexpr u32 Dollar { SDLK_DOLLAR };
|
||||
inline static constexpr u32 Percent { SDLK_PERCENT };
|
||||
inline static constexpr u32 Ampersand { SDLK_AMPERSAND };
|
||||
inline static constexpr u32 Apostrophe { SDLK_APOSTROPHE };
|
||||
inline static constexpr u32 Leftparen { SDLK_LEFTPAREN };
|
||||
inline static constexpr u32 Rightparen { SDLK_RIGHTPAREN };
|
||||
inline static constexpr u32 Asterisk { SDLK_ASTERISK };
|
||||
inline static constexpr u32 Plus { SDLK_PLUS };
|
||||
inline static constexpr u32 Comma { SDLK_COMMA };
|
||||
inline static constexpr u32 Minus { SDLK_MINUS };
|
||||
inline static constexpr u32 Period { SDLK_PERIOD };
|
||||
inline static constexpr u32 Slash { SDLK_SLASH };
|
||||
inline static constexpr u32 Num0 { SDLK_0 };
|
||||
inline static constexpr u32 Num1 { SDLK_1 };
|
||||
inline static constexpr u32 Num2 { SDLK_2 };
|
||||
inline static constexpr u32 Num3 { SDLK_3 };
|
||||
inline static constexpr u32 Num4 { SDLK_4 };
|
||||
inline static constexpr u32 Num5 { SDLK_5 };
|
||||
inline static constexpr u32 Num6 { SDLK_6 };
|
||||
inline static constexpr u32 Num7 { SDLK_7 };
|
||||
inline static constexpr u32 Num8 { SDLK_8 };
|
||||
inline static constexpr u32 Num9 { SDLK_9 };
|
||||
inline static constexpr u32 Colon { SDLK_COLON };
|
||||
inline static constexpr u32 Semicolon { SDLK_SEMICOLON };
|
||||
inline static constexpr u32 Less { SDLK_LESS };
|
||||
inline static constexpr u32 Equals { SDLK_EQUALS };
|
||||
inline static constexpr u32 Greater { SDLK_GREATER };
|
||||
inline static constexpr u32 Question { SDLK_QUESTION };
|
||||
inline static constexpr u32 At { SDLK_AT };
|
||||
inline static constexpr u32 LeftBracket { SDLK_LEFTBRACKET };
|
||||
inline static constexpr u32 Backslash { SDLK_BACKSLASH };
|
||||
inline static constexpr u32 RightBracket { SDLK_RIGHTBRACKET };
|
||||
inline static constexpr u32 Caret { SDLK_CARET };
|
||||
inline static constexpr u32 Underscore { SDLK_UNDERSCORE };
|
||||
inline static constexpr u32 Grave { SDLK_GRAVE };
|
||||
inline static constexpr u32 A { SDLK_A };
|
||||
inline static constexpr u32 B { SDLK_B };
|
||||
inline static constexpr u32 C { SDLK_C };
|
||||
inline static constexpr u32 D { SDLK_D };
|
||||
inline static constexpr u32 E { SDLK_E };
|
||||
inline static constexpr u32 F { SDLK_F };
|
||||
inline static constexpr u32 G { SDLK_G };
|
||||
inline static constexpr u32 H { SDLK_H };
|
||||
inline static constexpr u32 I { SDLK_I };
|
||||
inline static constexpr u32 J { SDLK_J };
|
||||
inline static constexpr u32 K { SDLK_K };
|
||||
inline static constexpr u32 L { SDLK_L };
|
||||
inline static constexpr u32 M { SDLK_M };
|
||||
inline static constexpr u32 N { SDLK_N };
|
||||
inline static constexpr u32 O { SDLK_O };
|
||||
inline static constexpr u32 P { SDLK_P };
|
||||
inline static constexpr u32 Q { SDLK_Q };
|
||||
inline static constexpr u32 R { SDLK_R };
|
||||
inline static constexpr u32 S { SDLK_S };
|
||||
inline static constexpr u32 T { SDLK_T };
|
||||
inline static constexpr u32 U { SDLK_U };
|
||||
inline static constexpr u32 V { SDLK_V };
|
||||
inline static constexpr u32 W { SDLK_W };
|
||||
inline static constexpr u32 X { SDLK_X };
|
||||
inline static constexpr u32 Y { SDLK_Y };
|
||||
inline static constexpr u32 Z { SDLK_Z };
|
||||
inline static constexpr u32 Leftbrace { SDLK_LEFTBRACE };
|
||||
inline static constexpr u32 Pipe { SDLK_PIPE };
|
||||
inline static constexpr u32 Rightbrace { SDLK_RIGHTBRACE };
|
||||
inline static constexpr u32 Tilde { SDLK_TILDE };
|
||||
inline static constexpr u32 Delete { SDLK_DELETE };
|
||||
inline static constexpr u32 Plusminus { SDLK_PLUSMINUS };
|
||||
inline static constexpr u32 Capslock { SDLK_CAPSLOCK };
|
||||
inline static constexpr u32 F1 { SDLK_F1 };
|
||||
inline static constexpr u32 F2 { SDLK_F2 };
|
||||
inline static constexpr u32 F3 { SDLK_F3 };
|
||||
inline static constexpr u32 F4 { SDLK_F4 };
|
||||
inline static constexpr u32 F5 { SDLK_F5 };
|
||||
inline static constexpr u32 F6 { SDLK_F6 };
|
||||
inline static constexpr u32 F7 { SDLK_F7 };
|
||||
inline static constexpr u32 F8 { SDLK_F8 };
|
||||
inline static constexpr u32 F9 { SDLK_F9 };
|
||||
inline static constexpr u32 F10 { SDLK_F10 };
|
||||
inline static constexpr u32 F11 { SDLK_F11 };
|
||||
inline static constexpr u32 F12 { SDLK_F12 };
|
||||
inline static constexpr u32 Printscreen { SDLK_PRINTSCREEN };
|
||||
inline static constexpr u32 Scrolllock { SDLK_SCROLLLOCK };
|
||||
inline static constexpr u32 Pause { SDLK_PAUSE };
|
||||
inline static constexpr u32 Insert { SDLK_INSERT };
|
||||
inline static constexpr u32 Home { SDLK_HOME };
|
||||
inline static constexpr u32 Pageup { SDLK_PAGEUP };
|
||||
inline static constexpr u32 End { SDLK_END };
|
||||
inline static constexpr u32 Pagedown { SDLK_PAGEDOWN };
|
||||
inline static constexpr u32 Right { SDLK_RIGHT };
|
||||
inline static constexpr u32 Left { SDLK_LEFT };
|
||||
inline static constexpr u32 Down { SDLK_DOWN };
|
||||
inline static constexpr u32 Up { SDLK_UP };
|
||||
inline static constexpr u32 Numlockclear { SDLK_NUMLOCKCLEAR };
|
||||
inline static constexpr u32 KpDivide { SDLK_KP_DIVIDE };
|
||||
inline static constexpr u32 KpMultiply { SDLK_KP_MULTIPLY };
|
||||
inline static constexpr u32 KpMinus { SDLK_KP_MINUS };
|
||||
inline static constexpr u32 KpPlus { SDLK_KP_PLUS };
|
||||
inline static constexpr u32 KpEnter { SDLK_KP_ENTER };
|
||||
inline static constexpr u32 Kp1 { SDLK_KP_1 };
|
||||
inline static constexpr u32 Kp2 { SDLK_KP_2 };
|
||||
inline static constexpr u32 Kp3 { SDLK_KP_3 };
|
||||
inline static constexpr u32 Kp4 { SDLK_KP_4 };
|
||||
inline static constexpr u32 Kp5 { SDLK_KP_5 };
|
||||
inline static constexpr u32 Kp6 { SDLK_KP_6 };
|
||||
inline static constexpr u32 Kp7 { SDLK_KP_7 };
|
||||
inline static constexpr u32 Kp8 { SDLK_KP_8 };
|
||||
inline static constexpr u32 Kp9 { SDLK_KP_9 };
|
||||
inline static constexpr u32 Kp0 { SDLK_KP_0 };
|
||||
inline static constexpr u32 KpPeriod { SDLK_KP_PERIOD };
|
||||
inline static constexpr u32 Application { SDLK_APPLICATION };
|
||||
inline static constexpr u32 Power { SDLK_POWER };
|
||||
inline static constexpr u32 KpEquals { SDLK_KP_EQUALS };
|
||||
inline static constexpr u32 F13 { SDLK_F13 };
|
||||
inline static constexpr u32 F14 { SDLK_F14 };
|
||||
inline static constexpr u32 F15 { SDLK_F15 };
|
||||
inline static constexpr u32 F16 { SDLK_F16 };
|
||||
inline static constexpr u32 F17 { SDLK_F17 };
|
||||
inline static constexpr u32 F18 { SDLK_F18 };
|
||||
inline static constexpr u32 F19 { SDLK_F19 };
|
||||
inline static constexpr u32 F20 { SDLK_F20 };
|
||||
inline static constexpr u32 F21 { SDLK_F21 };
|
||||
inline static constexpr u32 F22 { SDLK_F22 };
|
||||
inline static constexpr u32 F23 { SDLK_F23 };
|
||||
inline static constexpr u32 F24 { SDLK_F24 };
|
||||
inline static constexpr u32 Execute { SDLK_EXECUTE };
|
||||
inline static constexpr u32 Help { SDLK_HELP };
|
||||
inline static constexpr u32 Menu { SDLK_MENU };
|
||||
inline static constexpr u32 Select { SDLK_SELECT };
|
||||
inline static constexpr u32 Stop { SDLK_STOP };
|
||||
inline static constexpr u32 Again { SDLK_AGAIN };
|
||||
inline static constexpr u32 Undo { SDLK_UNDO };
|
||||
inline static constexpr u32 Cut { SDLK_CUT };
|
||||
inline static constexpr u32 Copy { SDLK_COPY };
|
||||
inline static constexpr u32 Paste { SDLK_PASTE };
|
||||
inline static constexpr u32 Find { SDLK_FIND };
|
||||
inline static constexpr u32 Mute { SDLK_MUTE };
|
||||
inline static constexpr u32 Volumeup { SDLK_VOLUMEUP };
|
||||
inline static constexpr u32 Volumedown { SDLK_VOLUMEDOWN };
|
||||
inline static constexpr u32 KpComma { SDLK_KP_COMMA };
|
||||
inline static constexpr u32 KpEqualsas400 { SDLK_KP_EQUALSAS400 };
|
||||
inline static constexpr u32 Alterase { SDLK_ALTERASE };
|
||||
inline static constexpr u32 Sysreq { SDLK_SYSREQ };
|
||||
inline static constexpr u32 Cancel { SDLK_CANCEL };
|
||||
inline static constexpr u32 Clear { SDLK_CLEAR };
|
||||
inline static constexpr u32 Prior { SDLK_PRIOR };
|
||||
inline static constexpr u32 Return2 { SDLK_RETURN2 };
|
||||
inline static constexpr u32 Separator { SDLK_SEPARATOR };
|
||||
inline static constexpr u32 Out { SDLK_OUT };
|
||||
inline static constexpr u32 Oper { SDLK_OPER };
|
||||
inline static constexpr u32 Clearagain { SDLK_CLEARAGAIN };
|
||||
inline static constexpr u32 Crsel { SDLK_CRSEL };
|
||||
inline static constexpr u32 Exsel { SDLK_EXSEL };
|
||||
inline static constexpr u32 Kp00 { SDLK_KP_00 };
|
||||
inline static constexpr u32 Kp000 { SDLK_KP_000 };
|
||||
inline static constexpr u32 Thousandsseparator { SDLK_THOUSANDSSEPARATOR };
|
||||
inline static constexpr u32 Decimalseparator { SDLK_DECIMALSEPARATOR };
|
||||
inline static constexpr u32 Currencyunit { SDLK_CURRENCYUNIT };
|
||||
inline static constexpr u32 Currencysubunit { SDLK_CURRENCYSUBUNIT };
|
||||
inline static constexpr u32 KpLeftparen { SDLK_KP_LEFTPAREN };
|
||||
inline static constexpr u32 KpRightparen { SDLK_KP_RIGHTPAREN };
|
||||
inline static constexpr u32 KpLeftbrace { SDLK_KP_LEFTBRACE };
|
||||
inline static constexpr u32 KpRightbrace { SDLK_KP_RIGHTBRACE };
|
||||
inline static constexpr u32 KpTab { SDLK_KP_TAB };
|
||||
inline static constexpr u32 KpBackspace { SDLK_KP_BACKSPACE };
|
||||
inline static constexpr u32 KpA { SDLK_KP_A };
|
||||
inline static constexpr u32 KpB { SDLK_KP_B };
|
||||
inline static constexpr u32 KpC { SDLK_KP_C };
|
||||
inline static constexpr u32 KpD { SDLK_KP_D };
|
||||
inline static constexpr u32 KpE { SDLK_KP_E };
|
||||
inline static constexpr u32 KpF { SDLK_KP_F };
|
||||
inline static constexpr u32 KpXor { SDLK_KP_XOR };
|
||||
inline static constexpr u32 KpPower { SDLK_KP_POWER };
|
||||
inline static constexpr u32 KpPercent { SDLK_KP_PERCENT };
|
||||
inline static constexpr u32 KpLess { SDLK_KP_LESS };
|
||||
inline static constexpr u32 KpGreater { SDLK_KP_GREATER };
|
||||
inline static constexpr u32 KpAmpersand { SDLK_KP_AMPERSAND };
|
||||
inline static constexpr u32 KpDblampersand { SDLK_KP_DBLAMPERSAND };
|
||||
inline static constexpr u32 KpVerticalbar { SDLK_KP_VERTICALBAR };
|
||||
inline static constexpr u32 KpDblverticalbar { SDLK_KP_DBLVERTICALBAR };
|
||||
inline static constexpr u32 KpColon { SDLK_KP_COLON };
|
||||
inline static constexpr u32 KpHash { SDLK_KP_HASH };
|
||||
inline static constexpr u32 KpSpace { SDLK_KP_SPACE };
|
||||
inline static constexpr u32 KpAt { SDLK_KP_AT };
|
||||
inline static constexpr u32 KpExclam { SDLK_KP_EXCLAM };
|
||||
inline static constexpr u32 KpMemstore { SDLK_KP_MEMSTORE };
|
||||
inline static constexpr u32 KpMemrecall { SDLK_KP_MEMRECALL };
|
||||
inline static constexpr u32 KpMemclear { SDLK_KP_MEMCLEAR };
|
||||
inline static constexpr u32 KpMemadd { SDLK_KP_MEMADD };
|
||||
inline static constexpr u32 KpMemsubtract { SDLK_KP_MEMSUBTRACT };
|
||||
inline static constexpr u32 KpMemmultiply { SDLK_KP_MEMMULTIPLY };
|
||||
inline static constexpr u32 KpMemdivide { SDLK_KP_MEMDIVIDE };
|
||||
inline static constexpr u32 KpPlusminus { SDLK_KP_PLUSMINUS };
|
||||
inline static constexpr u32 KpClear { SDLK_KP_CLEAR };
|
||||
inline static constexpr u32 KpClearentry { SDLK_KP_CLEARENTRY };
|
||||
inline static constexpr u32 KpBinary { SDLK_KP_BINARY };
|
||||
inline static constexpr u32 KpOctal { SDLK_KP_OCTAL };
|
||||
inline static constexpr u32 KpDecimal { SDLK_KP_DECIMAL };
|
||||
inline static constexpr u32 KpHexadecimal { SDLK_KP_HEXADECIMAL };
|
||||
inline static constexpr u32 Lctrl { SDLK_LCTRL };
|
||||
inline static constexpr u32 Lshift { SDLK_LSHIFT };
|
||||
inline static constexpr u32 Lalt { SDLK_LALT };
|
||||
inline static constexpr u32 Lgui { SDLK_LGUI };
|
||||
inline static constexpr u32 Rctrl { SDLK_RCTRL };
|
||||
inline static constexpr u32 Rshift { SDLK_RSHIFT };
|
||||
inline static constexpr u32 Ralt { SDLK_RALT };
|
||||
inline static constexpr u32 Rgui { SDLK_RGUI };
|
||||
inline static constexpr u32 Mode { SDLK_MODE };
|
||||
inline static constexpr u32 Sleep { SDLK_SLEEP };
|
||||
inline static constexpr u32 Wake { SDLK_WAKE };
|
||||
inline static constexpr u32 ChannelIncrement { SDLK_CHANNEL_INCREMENT };
|
||||
inline static constexpr u32 ChannelDecrement { SDLK_CHANNEL_DECREMENT };
|
||||
inline static constexpr u32 MediaPlay { SDLK_MEDIA_PLAY };
|
||||
inline static constexpr u32 MediaPause { SDLK_MEDIA_PAUSE };
|
||||
inline static constexpr u32 MediaRecord { SDLK_MEDIA_RECORD };
|
||||
inline static constexpr u32 MediaFastForward { SDLK_MEDIA_FAST_FORWARD };
|
||||
inline static constexpr u32 MediaRewind { SDLK_MEDIA_REWIND };
|
||||
inline static constexpr u32 MediaNextTrack { SDLK_MEDIA_NEXT_TRACK };
|
||||
inline static constexpr u32 MediaPreviousTrack { SDLK_MEDIA_PREVIOUS_TRACK };
|
||||
inline static constexpr u32 MediaStop { SDLK_MEDIA_STOP };
|
||||
inline static constexpr u32 MediaEject { SDLK_MEDIA_EJECT };
|
||||
inline static constexpr u32 MediaPlayPause { SDLK_MEDIA_PLAY_PAUSE };
|
||||
inline static constexpr u32 MediaSelect { SDLK_MEDIA_SELECT };
|
||||
inline static constexpr u32 AcNew { SDLK_AC_NEW };
|
||||
inline static constexpr u32 AcOpen { SDLK_AC_OPEN };
|
||||
inline static constexpr u32 AcClose { SDLK_AC_CLOSE };
|
||||
inline static constexpr u32 AcExit { SDLK_AC_EXIT };
|
||||
inline static constexpr u32 AcSave { SDLK_AC_SAVE };
|
||||
inline static constexpr u32 AcPrint { SDLK_AC_PRINT };
|
||||
inline static constexpr u32 AcProperties { SDLK_AC_PROPERTIES };
|
||||
inline static constexpr u32 AcSearch { SDLK_AC_SEARCH };
|
||||
inline static constexpr u32 AcHome { SDLK_AC_HOME };
|
||||
inline static constexpr u32 AcBack { SDLK_AC_BACK };
|
||||
inline static constexpr u32 AcForward { SDLK_AC_FORWARD };
|
||||
inline static constexpr u32 AcStop { SDLK_AC_STOP };
|
||||
inline static constexpr u32 AcRefresh { SDLK_AC_REFRESH };
|
||||
inline static constexpr u32 AcBookmarks { SDLK_AC_BOOKMARKS };
|
||||
inline static constexpr u32 Softleft { SDLK_SOFTLEFT };
|
||||
inline static constexpr u32 Softright { SDLK_SOFTRIGHT };
|
||||
inline static constexpr u32 Call { SDLK_CALL };
|
||||
inline static constexpr u32 Endcall { SDLK_ENDCALL };
|
||||
inline static constexpr u32 LeftTab { SDLK_LEFT_TAB };
|
||||
inline static constexpr u32 Level5Shift { SDLK_LEVEL5_SHIFT };
|
||||
inline static constexpr u32 MultiKeyCompose { SDLK_MULTI_KEY_COMPOSE };
|
||||
inline static constexpr u32 Lmeta { SDLK_LMETA };
|
||||
inline static constexpr u32 Rmeta { SDLK_RMETA };
|
||||
inline static constexpr u32 Lhyper { SDLK_LHYPER };
|
||||
inline static constexpr u32 Rhyper { SDLK_RHYPER };
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,6 +325,13 @@ namespace ostd
|
|||
move_cursor_to(cast<u32>(m_buffer.len()), extend);
|
||||
}
|
||||
|
||||
void TextBuffer::setCursorByteOffset(u32 byte_offset, bool extend)
|
||||
{
|
||||
const u32 clamped = clamp_to_codepoint_boundary(byte_offset);
|
||||
move_cursor_to(clamped, extend);
|
||||
m_desiredColumn = -1;
|
||||
}
|
||||
|
||||
|
||||
// ============================================================
|
||||
// Mutation
|
||||
|
|
@ -602,6 +609,50 @@ namespace ostd
|
|||
}
|
||||
}
|
||||
|
||||
void TextBuffer::selectWordAt(u32 byte_offset)
|
||||
{
|
||||
m_desiredColumn = -1;
|
||||
if (m_buffer.empty()) return;
|
||||
|
||||
// Clamp to a valid codepoint boundary first. If we're past end, back up
|
||||
// to the last codepoint so we have something to classify.
|
||||
u32 pos = clamp_to_codepoint_boundary(byte_offset);
|
||||
if (pos >= m_buffer.len()) {
|
||||
// At end-of-buffer — step back one codepoint to get something to
|
||||
// classify, then expand from there.
|
||||
if (m_buffer.len() == 0) return;
|
||||
pos = String::utf8::prev_codepoint_start(m_buffer.cpp_str(), m_buffer.len());
|
||||
}
|
||||
|
||||
// Expand left and right from `pos`. The trick: prev_word_boundary and
|
||||
// next_word_boundary are *almost* what we want, but they're designed
|
||||
// for "skip the run AND following whitespace" semantics. For word
|
||||
// *selection*, we want just the run.
|
||||
//
|
||||
// Easiest: classify the byte at pos, then walk outward in both
|
||||
// directions while the class matches.
|
||||
|
||||
const cpp_string& s = m_buffer.cpp_str();
|
||||
const WordClass cls = classify_byte(cast<u8>(s[pos]));
|
||||
|
||||
// Walk left: advance start backward while previous codepoint is same class.
|
||||
u32 start = pos;
|
||||
while (start > 0) {
|
||||
const u32 prev = String::utf8::prev_codepoint_start(s, start);
|
||||
if (classify_byte(cast<u8>(s[prev])) != cls) break;
|
||||
start = prev;
|
||||
}
|
||||
|
||||
// Walk right: advance end forward while current codepoint is same class.
|
||||
u32 end = pos;
|
||||
while (end < s.size()) {
|
||||
if (classify_byte(cast<u8>(s[end])) != cls) break;
|
||||
end = String::utf8::next_codepoint_start(s, end);
|
||||
}
|
||||
|
||||
selectRange(start, end);
|
||||
}
|
||||
|
||||
u32 TextBuffer::prev_word_boundary(u32 byte_offset) const
|
||||
{
|
||||
const cpp_string& s = m_buffer.cpp_str();
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ namespace ostd
|
|||
String selectedText(void) const;
|
||||
void clearSelection(void);
|
||||
void selectAll(void);
|
||||
void selectWordAt(u32 byte_offset);
|
||||
void selectRange(u32 start_byte, u32 end_byte);
|
||||
|
||||
// ===================== Cursor movement ====================
|
||||
|
|
@ -108,6 +109,7 @@ namespace ostd
|
|||
void moveLineEnd(bool extend = false);
|
||||
void moveDocumentStart(bool extend = false);
|
||||
void moveDocumentEnd(bool extend = false);
|
||||
void setCursorByteOffset(u32 byte_offset, bool extend);
|
||||
|
||||
// ======================== Mutation ========================
|
||||
// All mutators replace the current selection (if any) before
|
||||
|
|
|
|||
|
|
@ -1,21 +1,21 @@
|
|||
/*
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
OmniaFramework - A collection of useful functionality
|
||||
Copyright (C) 2026 OmniaX-Dev
|
||||
|
||||
This file is part of OmniaFramework.
|
||||
This file is part of OmniaFramework.
|
||||
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
OmniaFramework is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
OmniaFramework is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with OmniaFramework. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "utils/Keycodes.hpp"
|
||||
|
|
@ -29,9 +29,8 @@ class Window : public ogfx::GraphicsWindow
|
|||
inline Window(void) { }
|
||||
inline void onInitialize(void) override
|
||||
{
|
||||
connectSignal(ogfx::gui::RawTextInput::actionEventSignalID);
|
||||
m_gfx.init(*this);
|
||||
}
|
||||
}
|
||||
|
||||
inline void handleSignal(ostd::Signal& signal) override
|
||||
{
|
||||
|
|
@ -71,7 +70,7 @@ i32 main(i32 argc, char** argv)
|
|||
{
|
||||
Window window;
|
||||
window.initialize(800, 600, "OmniaFramework - Test Window");
|
||||
window.setClearColor({ 0, 2 , 15 });
|
||||
window.setClearColor({ 0, 2 , 15 });
|
||||
|
||||
while (window.isRunning())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -229,6 +229,7 @@ class TestWindow : public Window
|
|||
t1.addWidget(m_list, { 30, 300 });
|
||||
t1.addWidget(m_panel2, { 500, 100 });
|
||||
t1.addWidget(m_combo, { 400, 600 });
|
||||
t1.addWidget(m_text, { 400, 700 });
|
||||
|
||||
t2.setLayout<BoxLayout>(BoxLayout::Orientation::Vertical);
|
||||
t2.getLayout()->setSpacing(16);
|
||||
|
|
@ -390,6 +391,7 @@ class TestWindow : public Window
|
|||
Label m_cacheHitsLbl { *this };
|
||||
Label m_cacheMissesLbl { *this };
|
||||
ComboBox m_combo { *this };
|
||||
TextEdit m_text { *this };
|
||||
|
||||
enum MenuId : i32 { New = 1, Open, Save, SaveAs, Exit, CopyRaw, CopyFormatted };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue