animated example
This commit is contained in:
parent
216972e73a
commit
c9fd1b1585
17 changed files with 576 additions and 465 deletions
|
|
@ -454,16 +454,32 @@ void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, ui
|
|||
std::string buf;
|
||||
buf.resize(16);
|
||||
int n = xkb_state_key_get_utf8(xkb_state, keycode, buf.data(), 16);
|
||||
if (n > 0) {
|
||||
if ((unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
|
||||
buf.resize(n);
|
||||
focusedWindow->onTextInput.Invoke(buf);
|
||||
}
|
||||
std::string utf8;
|
||||
if (n > 0 && (unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
|
||||
buf.resize(n);
|
||||
utf8 = buf;
|
||||
focusedWindow->onTextInput.Invoke(utf8);
|
||||
}
|
||||
|
||||
// Replace the active repeat with this key — most recent press wins,
|
||||
// matching xkbcommon's typical behaviour and most desktop apps.
|
||||
keyRepeat.active = (keyRepeat.rate > 0);
|
||||
keyRepeat.key = crafterKey;
|
||||
keyRepeat.utf8 = std::move(utf8);
|
||||
keyRepeat.pressTime = std::chrono::steady_clock::now();
|
||||
keyRepeat.lastFireTime = keyRepeat.pressTime;
|
||||
} else {
|
||||
focusedWindow->heldkeys[(std::uint8_t)crafterKey] = false;
|
||||
focusedWindow->onKeyUp[(std::uint8_t)crafterKey].Invoke();
|
||||
focusedWindow->onAnyKeyUp.Invoke(crafterKey);
|
||||
|
||||
// If the released key was the one repeating, stop. Otherwise leave
|
||||
// the existing repeat alone (user pressed/released a modifier
|
||||
// mid-repeat etc.).
|
||||
if (keyRepeat.active && keyRepeat.key == crafterKey) {
|
||||
keyRepeat.active = false;
|
||||
keyRepeat.utf8.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -472,7 +488,36 @@ void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t seri
|
|||
}
|
||||
|
||||
void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) {
|
||||
keyRepeat.rate = rate;
|
||||
keyRepeat.delay = delay;
|
||||
if (rate <= 0) keyRepeat.active = false; // compositor disabled repeat
|
||||
}
|
||||
|
||||
void Device::TickKeyRepeats() {
|
||||
if (!keyRepeat.active || !focusedWindow) return;
|
||||
if (keyRepeat.rate <= 0) return;
|
||||
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
using ms = std::chrono::milliseconds;
|
||||
auto sincePress = std::chrono::duration_cast<ms>(now - keyRepeat.pressTime).count();
|
||||
if (sincePress < keyRepeat.delay) return;
|
||||
|
||||
auto period = std::chrono::milliseconds(1000 / keyRepeat.rate);
|
||||
auto sinceLastFire = std::chrono::duration_cast<ms>(now - keyRepeat.lastFireTime).count();
|
||||
if (sinceLastFire < period.count()) return;
|
||||
|
||||
// Catch up — emit one event per missed period so a paused frame doesn't
|
||||
// make the repeat permanently lag behind.
|
||||
while (now - keyRepeat.lastFireTime >= period) {
|
||||
focusedWindow->onKeyDown[(std::uint8_t)keyRepeat.key].Invoke();
|
||||
focusedWindow->onAnyKeyDown.Invoke(keyRepeat.key);
|
||||
focusedWindow->onKeyHold[(std::uint8_t)keyRepeat.key].Invoke();
|
||||
focusedWindow->onAnyKeyHold.Invoke(keyRepeat.key);
|
||||
if (!keyRepeat.utf8.empty()) {
|
||||
focusedWindow->onTextInput.Invoke(keyRepeat.utf8);
|
||||
}
|
||||
keyRepeat.lastFireTime += period;
|
||||
}
|
||||
}
|
||||
|
||||
void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) {
|
||||
|
|
|
|||
|
|
@ -123,10 +123,14 @@ void UIScene::Initialize(Window& window, const std::filesystem::path& spvPath) {
|
|||
}
|
||||
);
|
||||
|
||||
// Per-frame: re-layout, emit, push items.
|
||||
// Per-frame: re-layout, emit, push items. We capture FrameTime here
|
||||
// so we can advance the scene's clock (caret blink, animations).
|
||||
updateListener_ = std::make_unique<EventListener<FrameTime>>(
|
||||
&window.onUpdate,
|
||||
[this](FrameTime) { RebuildFrame(); }
|
||||
[this](FrameTime ft) {
|
||||
elapsedSec_ += static_cast<float>(ft.delta.count());
|
||||
RebuildFrame();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +157,7 @@ void UIScene::RebuildFrame() {
|
|||
drawList.atlas = &renderer.atlas;
|
||||
drawList.bindlessBaseHeapIdx = renderer.BindlessBaseHeapIdx();
|
||||
drawList.scale = sc;
|
||||
drawList.time = elapsedSec_;
|
||||
if (background_) {
|
||||
drawList.AddRect(
|
||||
{ 0, 0, static_cast<float>(window_->width), static_cast<float>(window_->height) },
|
||||
|
|
|
|||
|
|
@ -724,6 +724,10 @@ void Window::Render() {
|
|||
|
||||
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||||
|
||||
// Synthesise key-repeat events before listeners run, so the focused
|
||||
// widget's OnTextInput / OnKeyDown sees them in the same frame.
|
||||
Device::TickKeyRepeats();
|
||||
|
||||
onUpdate.Invoke({startTime, startTime-lastFrameBegin});
|
||||
#ifdef CRAFTER_TIMING
|
||||
totalUpdate = std::chrono::nanoseconds(0);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue