Merge pull request #173 from faissaloo/serenity-keys

SerenityKeys
This commit is contained in:
Andreas Kling 2019-06-16 22:37:33 +02:00 committed by GitHub
commit 940eb1bbeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 5 deletions

View file

@ -31,7 +31,7 @@ void GAbstractButton::set_checked(bool checked)
m_checked = checked;
if (is_exclusive() && checked) {
parent_widget()->for_each_child_of_type<GAbstractButton>([&] (auto& sibling) {
parent_widget()->for_each_child_of_type<GAbstractButton>([&](auto& sibling) {
if (!sibling.is_exclusive() || !sibling.is_checkable() || !sibling.is_checked())
return IterationDecision::Continue;
sibling.m_checked = false;

View file

@ -29,6 +29,7 @@ public:
virtual void click() = 0;
virtual const char* class_name() const override { return "GAbstractButton"; }
virtual bool accepts_focus() const override { return true; }
virtual bool accepts_keyboard_select() const { return true; }
protected:
explicit GAbstractButton(GWidget* parent);

View file

@ -66,6 +66,11 @@ void GButton::click()
on_click(*this);
}
bool GButton::accepts_keyboard_select() const
{
return is_enabled();
}
void GButton::set_action(GAction& action)
{
m_action = action.make_weak_ptr();

View file

@ -33,6 +33,7 @@ public:
virtual const char* class_name() const override { return "GButton"; }
virtual bool accepts_focus() const override { return true; }
virtual bool accepts_keyboard_select() const;
protected:
virtual void paint_event(GPaintEvent&) override;

View file

@ -102,6 +102,7 @@ public:
void update(const Rect&);
virtual bool accepts_focus() const { return false; }
virtual bool accepts_keyboard_select() const { return false; }
bool is_focused() const;
void set_focus(bool);

View file

@ -3,6 +3,7 @@
#include "GEventLoop.h"
#include "GWidget.h"
#include <AK/HashMap.h>
#include <AK/StringBuilder.h>
#include <LibC/stdio.h>
#include <LibC/stdlib.h>
#include <LibC/unistd.h>
@ -241,6 +242,8 @@ void GWindow::event(CEvent& event)
for (auto& rect : rects)
m_main_widget->event(*make<GPaintEvent>(rect));
paint_keybinds();
if (m_double_buffering_enabled)
flip(rects);
else if (created_new_backing_store)
@ -262,14 +265,53 @@ void GWindow::event(CEvent& event)
}
if (event.type() == GEvent::KeyUp || event.type() == GEvent::KeyDown) {
if (m_focused_widget)
return m_focused_widget->event(event);
if (m_main_widget)
return m_main_widget->event(event);
auto keyevent = static_cast<GKeyEvent&>(event);
if (keyevent.logo() && event.type() == GEvent::KeyUp) {
if (m_keybind_mode) {
m_keybind_mode = false;
} else {
m_keybind_mode = true;
find_keyboard_selectable();
m_entered_keybind = "";
}
update();
return;
}
if (m_keybind_mode) {
if (event.type() == GEvent::KeyUp) {
StringBuilder builder;
builder.append(m_entered_keybind);
builder.append(keyevent.text());
m_entered_keybind = builder.to_string();
auto found_widget = m_hashed_potential_keybind_widgets.find(m_entered_keybind);
if (found_widget != m_hashed_potential_keybind_widgets.end()) {
m_keybind_mode = false;
auto event = make<GMouseEvent>(GEvent::MouseDown, Point(), 0, GMouseButton::Left, 0, 0);
found_widget->value->event(*event);
event = make<GMouseEvent>(GEvent::MouseUp, Point(), 0, GMouseButton::Left, 0, 0);
found_widget->value->event(*event);
} else if (m_entered_keybind.length() >= m_max_keybind_length) {
m_keybind_mode = false;
}
update();
}
} else {
if (m_focused_widget)
return m_focused_widget->event(event);
if (m_main_widget)
return m_main_widget->event(event);
}
return;
}
if (event.type() == GEvent::WindowBecameActive || event.type() == GEvent::WindowBecameInactive) {
if (event.type() == GEvent::WindowBecameInactive && m_keybind_mode) {
m_keybind_mode = false;
update();
}
m_is_active = event.type() == GEvent::WindowBecameActive;
if (m_main_widget)
m_main_widget->event(event);
@ -307,6 +349,72 @@ void GWindow::event(CEvent& event)
CObject::event(event);
}
void GWindow::paint_keybinds()
{
if (!m_keybind_mode)
return;
GPainter painter(*m_main_widget);
for (auto& keypair : m_hashed_potential_keybind_widgets) {
auto widget = keypair.value;
bool could_be_keybind = true;
for (size_t i = 0; i < m_entered_keybind.length(); i++) {
if (keypair.key.characters()[i] != m_entered_keybind.characters()[i]) {
could_be_keybind = false;
}
}
if (could_be_keybind) {
auto rect = Rect(widget->x() - 5, widget->y() - 5, 4 + Font::default_font().width(keypair.key), 16);
auto highlight_rect = Rect(widget->x() - 3, widget->y() - 5, 0, 16);
painter.fill_rect(rect, Color::LightGray);
painter.draw_rect(rect, Color::Black, false);
painter.draw_text(rect, keypair.key.characters(), TextAlignment::Center, Color::Black);
painter.draw_text(highlight_rect, m_entered_keybind.characters(), TextAlignment::CenterLeft, Color::MidGray);
}
}
}
void GWindow::find_keyboard_selectable()
{
Vector<GWidget*> potential_keybind_widgets;
m_hashed_potential_keybind_widgets.clear();
find_keyboard_selectable_children(m_main_widget, potential_keybind_widgets);
m_max_keybind_length = ceil_div(potential_keybind_widgets.size(), ('z' - 'a'));
size_t buffer_length = m_max_keybind_length + 1;
char keybind_buffer[buffer_length];
for (size_t i = 0; i < buffer_length - 1; i++) {
keybind_buffer[i] = 'a';
}
keybind_buffer[buffer_length - 1] = '\0';
for (auto& widget : potential_keybind_widgets) {
m_hashed_potential_keybind_widgets.set(String(keybind_buffer), widget);
for (size_t i = 0; i < buffer_length - 1; i++) {
if (keybind_buffer[i] >= 'z') {
keybind_buffer[i] = 'a';
} else {
keybind_buffer[i]++;
break;
}
}
}
}
void GWindow::find_keyboard_selectable_children(GWidget* widget, Vector<GWidget*>& potential_keybind_widgets)
{
widget->for_each_child_widget([&](auto& child) {
if (child.accepts_keyboard_select()) {
potential_keybind_widgets.append(&child);
find_keyboard_selectable_children(&child, potential_keybind_widgets);
}
return IterationDecision::Continue;
});
}
bool GWindow::is_visible() const
{
return false;
@ -316,6 +424,7 @@ void GWindow::update(const Rect& a_rect)
{
if (!m_window_id)
return;
for (auto& pending_rect : m_pending_paint_event_rects) {
if (pending_rect.contains(a_rect)) {
#ifdef UPDATE_COALESCING_DEBUG

View file

@ -1,6 +1,7 @@
#pragma once
#include <AK/AKString.h>
#include <AK/HashMap.h>
#include <AK/WeakPtr.h>
#include <LibCore/CObject.h>
#include <LibGUI/GWindowType.h>
@ -130,6 +131,10 @@ protected:
private:
virtual bool is_window() const override final { return true; }
void paint_keybinds();
void find_keyboard_selectable();
void find_keyboard_selectable_children(GWidget* widget, Vector<GWidget*>& potential_keybind_widgets);
Retained<GraphicsBitmap> create_backing_bitmap(const Size&);
void set_current_backing_bitmap(GraphicsBitmap&, bool flush_immediately = false);
void flip(const Vector<Rect, 32>& dirty_rects);
@ -159,4 +164,8 @@ private:
bool m_resizable { true };
bool m_fullscreen { false };
bool m_show_titlebar { true };
bool m_keybind_mode { false };
String m_entered_keybind;
size_t m_max_keybind_length { 0 };
HashMap<String, GWidget*> m_hashed_potential_keybind_widgets;
};