1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 08:08:12 +00:00
serenity/Userland/Applications/CharacterMap/CharacterMapWidget.cpp
Sam Atkins 2a7c638cd9 CharacterMap: Add a find-by-name window
This works the same way as the command-line usage, searching against the
display name as provided by LibUnicode.

I've modified the search loop to cover every possible unicode
code-point, since my previous logic was flawed. Code-points are not
dense, there are gaps, so simply iterating up to the count of them will
skip ones with higher values. Surprisingly, iterating all 1,114,112 of
them still runs in a third of a second. Computers are fast!
2022-01-16 11:17:03 +01:00

155 lines
6.7 KiB
C++

/*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CharacterMapWidget.h"
#include "CharacterSearchWidget.h"
#include <AK/StringUtils.h>
#include <Applications/CharacterMap/CharacterMapWindowGML.h>
#include <LibConfig/Client.h>
#include <LibGUI/Action.h>
#include <LibGUI/Application.h>
#include <LibGUI/Clipboard.h>
#include <LibGUI/FontPicker.h>
#include <LibGUI/Icon.h>
#include <LibGUI/InputBox.h>
#include <LibGUI/Label.h>
#include <LibGUI/Menu.h>
#include <LibGUI/TextBox.h>
#include <LibGUI/Toolbar.h>
#include <LibUnicode/CharacterTypes.h>
CharacterMapWidget::CharacterMapWidget()
{
load_from_gml(character_map_window_gml);
m_toolbar = find_descendant_of_type_named<GUI::Toolbar>("toolbar");
m_font_name_label = find_descendant_of_type_named<GUI::Label>("font_name");
m_glyph_map = find_descendant_of_type_named<GUI::GlyphMapWidget>("glyph_map");
m_output_box = find_descendant_of_type_named<GUI::TextBox>("output_box");
m_copy_output_button = find_descendant_of_type_named<GUI::Button>("copy_output_button");
m_statusbar = find_descendant_of_type_named<GUI::Statusbar>("statusbar");
m_choose_font_action = GUI::Action::create("Choose Font...", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/app-font-editor.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
auto font_picker = GUI::FontPicker::construct(window(), &font(), false);
if (font_picker->exec() == GUI::Dialog::ExecOK) {
auto& font = *font_picker->font();
Config::write_string("CharacterMap", "History", "Font", font.qualified_name());
set_font(font);
}
});
m_copy_selection_action = GUI::CommonActions::make_copy_action([&](GUI::Action&) {
auto selection = m_glyph_map->selection();
StringBuilder builder;
for (auto code_point = selection.start(); code_point < selection.start() + selection.size(); ++code_point) {
if (!m_glyph_map->font().contains_glyph(code_point))
continue;
builder.append_code_point(code_point);
}
GUI::Clipboard::the().set_plain_text(builder.to_string());
});
m_copy_selection_action->set_status_tip("Copy the highlighted characters to the clipboard");
m_previous_glyph_action = GUI::Action::create("Previous character", { Mod_Alt, Key_Left }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-back.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
m_glyph_map->select_previous_existing_glyph();
});
m_previous_glyph_action->set_status_tip("Seek the previous visible glyph");
m_next_glyph_action = GUI::Action::create("&Next Glyph", { Mod_Alt, Key_Right }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-forward.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
m_glyph_map->select_next_existing_glyph();
});
m_next_glyph_action->set_status_tip("Seek the next visible glyph");
m_go_to_glyph_action = GUI::Action::create("Go to glyph...", { Mod_Ctrl, Key_G }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/go-to.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
String input;
if (GUI::InputBox::show(window(), input, "Hexadecimal:", "Go to glyph") == GUI::InputBox::ExecOK && !input.is_empty()) {
auto maybe_code_point = AK::StringUtils::convert_to_uint_from_hex(input);
if (!maybe_code_point.has_value())
return;
auto code_point = clamp(maybe_code_point.value(), 0x0000, 0x10FFFF);
m_glyph_map->set_focus(true);
m_glyph_map->set_active_glyph(code_point);
m_glyph_map->scroll_to_glyph(code_point);
}
});
m_go_to_glyph_action->set_status_tip("Go to the specified code point");
m_find_glyphs_action = GUI::Action::create("&Find glyphs...", { Mod_Ctrl, Key_F }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/find.png").release_value_but_fixme_should_propagate_errors(), [&](auto&) {
if (m_find_window.is_null()) {
m_find_window = GUI::Window::construct(window());
auto& search_widget = m_find_window->set_main_widget<CharacterSearchWidget>();
search_widget.on_character_selected = [&](auto code_point) {
m_glyph_map->set_active_glyph(code_point);
m_glyph_map->scroll_to_glyph(code_point);
};
m_find_window->set_icon(GUI::Icon::try_create_default_icon("find").value().bitmap_for_size(16));
m_find_window->set_title("Find a character");
m_find_window->resize(300, 400);
}
m_find_window->show();
m_find_window->move_to_front();
m_find_window->find_descendant_of_type_named<GUI::TextBox>("search_input")->set_focus(true);
});
m_toolbar->add_action(*m_choose_font_action);
m_toolbar->add_separator();
m_toolbar->add_action(*m_copy_selection_action);
m_toolbar->add_separator();
m_toolbar->add_action(*m_previous_glyph_action);
m_toolbar->add_action(*m_next_glyph_action);
m_toolbar->add_action(*m_go_to_glyph_action);
m_toolbar->add_action(*m_find_glyphs_action);
m_glyph_map->on_active_glyph_changed = [&](int) {
update_statusbar();
};
m_glyph_map->on_glyph_double_clicked = [&](int code_point) {
StringBuilder builder;
builder.append(m_output_box->text());
builder.append_code_point(code_point);
m_output_box->set_text(builder.string_view());
};
m_copy_output_button->on_click = [&](auto) {
GUI::Clipboard::the().set_plain_text(m_output_box->text());
};
did_change_font();
update_statusbar();
}
CharacterMapWidget::~CharacterMapWidget()
{
}
void CharacterMapWidget::initialize_menubar(GUI::Window& window)
{
auto& file_menu = window.add_menu("&File");
file_menu.add_action(GUI::CommonActions::make_quit_action([](GUI::Action&) {
GUI::Application::the()->quit();
}));
auto& help_menu = window.add_menu("&Help");
help_menu.add_action(GUI::CommonActions::make_about_action("Character Map", GUI::Icon::default_icon("app-keyboard-settings"), &window));
}
void CharacterMapWidget::did_change_font()
{
m_glyph_map->set_font(font());
m_font_name_label->set_text(font().qualified_name());
m_output_box->set_font(font());
}
void CharacterMapWidget::update_statusbar()
{
auto code_point = m_glyph_map->active_glyph();
StringBuilder builder;
builder.appendff("U+{:04X}", code_point);
if (auto display_name = Unicode::code_point_display_name(code_point); display_name.has_value())
builder.appendff(" - {}", display_name.value());
m_statusbar->set_text(builder.to_string());
}