summaryrefslogtreecommitdiff
path: root/Userland/Applications
diff options
context:
space:
mode:
authorSam Atkins <atkinssj@serenityos.org>2022-01-10 15:59:42 +0000
committerAndreas Kling <kling@serenityos.org>2022-01-16 11:17:03 +0100
commit2327ea897071a51322c42fc17ba0184ab49d4546 (patch)
treeee5d6ab563141eeeab993376082a76db02fceda6 /Userland/Applications
parent1d79e8cb0f3a27204921c5fe0ac022ec9d76b43f (diff)
downloadserenity-2327ea897071a51322c42fc17ba0184ab49d4546.zip
CharacterMap: Add new Character Map application :^)
Diffstat (limited to 'Userland/Applications')
-rw-r--r--Userland/Applications/CMakeLists.txt1
-rw-r--r--Userland/Applications/CharacterMap/CMakeLists.txt17
-rw-r--r--Userland/Applications/CharacterMap/CharacterMapWidget.cpp90
-rw-r--r--Userland/Applications/CharacterMap/CharacterMapWidget.h34
-rw-r--r--Userland/Applications/CharacterMap/CharacterMapWindow.gml44
-rw-r--r--Userland/Applications/CharacterMap/main.cpp88
6 files changed, 274 insertions, 0 deletions
diff --git a/Userland/Applications/CMakeLists.txt b/Userland/Applications/CMakeLists.txt
index ddd04fe00e..b9ccd64c6e 100644
--- a/Userland/Applications/CMakeLists.txt
+++ b/Userland/Applications/CMakeLists.txt
@@ -6,6 +6,7 @@ add_subdirectory(Browser)
add_subdirectory(BrowserSettings)
add_subdirectory(Calculator)
add_subdirectory(Calendar)
+add_subdirectory(CharacterMap)
add_subdirectory(CrashReporter)
add_subdirectory(Debugger)
add_subdirectory(DisplaySettings)
diff --git a/Userland/Applications/CharacterMap/CMakeLists.txt b/Userland/Applications/CharacterMap/CMakeLists.txt
new file mode 100644
index 0000000000..d675fba38e
--- /dev/null
+++ b/Userland/Applications/CharacterMap/CMakeLists.txt
@@ -0,0 +1,17 @@
+serenity_component(
+ CharacterMap
+ REQUIRED
+ TARGETS CharacterMap
+)
+
+compile_gml(CharacterMapWindow.gml CharacterMapWindowGML.h character_map_window_gml)
+
+set(SOURCES
+ CharacterMapWindowGML.h
+ CharacterMapWidget.cpp
+ main.cpp
+)
+
+serenity_app(CharacterMap ICON app-keyboard-settings)
+target_link_libraries(CharacterMap LibGUI LibMain)
+link_with_unicode_data(CharacterMap)
diff --git a/Userland/Applications/CharacterMap/CharacterMapWidget.cpp b/Userland/Applications/CharacterMap/CharacterMapWidget.cpp
new file mode 100644
index 0000000000..9c60da89cd
--- /dev/null
+++ b/Userland/Applications/CharacterMap/CharacterMapWidget.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "CharacterMapWidget.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/Label.h>
+#include <LibGUI/Menu.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_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_toolbar->add_action(*m_choose_font_action);
+ m_toolbar->add_separator();
+ m_toolbar->add_action(*m_copy_selection_action);
+
+ m_glyph_map->on_active_glyph_changed = [&](int) {
+ update_statusbar();
+ };
+
+ 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());
+}
+
+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());
+}
diff --git a/Userland/Applications/CharacterMap/CharacterMapWidget.h b/Userland/Applications/CharacterMap/CharacterMapWidget.h
new file mode 100644
index 0000000000..bf4e881017
--- /dev/null
+++ b/Userland/Applications/CharacterMap/CharacterMapWidget.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <LibGUI/Button.h>
+#include <LibGUI/GlyphMapWidget.h>
+#include <LibGUI/Statusbar.h>
+
+class CharacterMapWidget final : public GUI::Widget {
+ C_OBJECT(CharacterMapWidget);
+
+public:
+ virtual ~CharacterMapWidget() override;
+
+ void initialize_menubar(GUI::Window& window);
+
+private:
+ CharacterMapWidget();
+
+ virtual void did_change_font() override;
+ void update_statusbar();
+
+ RefPtr<GUI::Toolbar> m_toolbar;
+ RefPtr<GUI::Label> m_font_name_label;
+ RefPtr<GUI::GlyphMapWidget> m_glyph_map;
+ RefPtr<GUI::Statusbar> m_statusbar;
+
+ RefPtr<GUI::Action> m_choose_font_action;
+ RefPtr<GUI::Action> m_copy_selection_action;
+};
diff --git a/Userland/Applications/CharacterMap/CharacterMapWindow.gml b/Userland/Applications/CharacterMap/CharacterMapWindow.gml
new file mode 100644
index 0000000000..3824614848
--- /dev/null
+++ b/Userland/Applications/CharacterMap/CharacterMapWindow.gml
@@ -0,0 +1,44 @@
+@GUI::Frame {
+ fill_with_background_color: true
+
+ layout: @GUI::VerticalBoxLayout {
+ }
+
+ @GUI::ToolbarContainer {
+ @GUI::Toolbar {
+ layout: @GUI::HorizontalBoxLayout {
+ spacing: 4
+ margins: [0, 0, 0, 4]
+ }
+
+ name: "toolbar"
+
+ @GUI::Label {
+ text: "Font: "
+ autosize: true
+ }
+
+ @GUI::Frame {
+ background_role: "Base"
+ fill_with_background_color: true
+ fixed_height: 20
+
+ layout: @GUI::VerticalBoxLayout {
+ }
+
+ @GUI::Label {
+ name: "font_name"
+ text: "Cool Font 16 400"
+ }
+ }
+ }
+ }
+
+ @GUI::GlyphMapWidget {
+ name: "glyph_map"
+ }
+
+ @GUI::Statusbar {
+ name: "statusbar"
+ }
+}
diff --git a/Userland/Applications/CharacterMap/main.cpp b/Userland/Applications/CharacterMap/main.cpp
new file mode 100644
index 0000000000..2764a797a5
--- /dev/null
+++ b/Userland/Applications/CharacterMap/main.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "CharacterMapWidget.h"
+#include <LibConfig/Client.h>
+#include <LibCore/ArgsParser.h>
+#include <LibCore/System.h>
+#include <LibGUI/Application.h>
+#include <LibGUI/Icon.h>
+#include <LibGUI/Window.h>
+#include <LibGfx/FontDatabase.h>
+#include <LibMain/Main.h>
+#include <LibUnicode/CharacterTypes.h>
+
+static void search_and_print_results(String const& query)
+{
+ outln("Searching for '{}'", query);
+ String uppercase_query = query.to_uppercase();
+ StringView uppercase_query_view = uppercase_query.view();
+ // FIXME: At time of writing there are 144,697 code points in Unicode. I've added some breathing room,
+ // but ideally this would be defined in LibUnicode somewhere.
+ constexpr u32 unicode_character_count = 150000;
+ // FIXME: There's probably a better way to do this than just looping, but it still only takes ~150ms to run for me!
+ u32 result_count = 0;
+ for (u32 i = 1; i < unicode_character_count; ++i) {
+ if (auto maybe_display_name = Unicode::code_point_display_name(i); maybe_display_name.has_value()) {
+ auto& display_name = maybe_display_name.value();
+ // FIXME: This should be a case-sensitive search, since we already converted the query to uppercase
+ // and the unicode names are all in uppercase. But, that makes it run slower!
+ // Sensitive: ~175ms, Insensitive: ~140ms
+ if (display_name.contains(uppercase_query_view, AK::CaseSensitivity::CaseInsensitive)) {
+ StringBuilder builder;
+ builder.append_code_point(i);
+ builder.append(" - ");
+ builder.append(display_name);
+ outln(builder.string_view());
+ result_count++;
+ }
+ }
+ }
+
+ if (result_count == 0)
+ outln("No results found.");
+ else if (result_count == 1)
+ outln("1 result found.");
+ else
+ outln("{} results found.", result_count);
+}
+
+ErrorOr<int> serenity_main(Main::Arguments arguments)
+{
+ TRY(Core::System::pledge("stdio recvfd sendfd rpath unix"));
+
+ auto app = TRY(GUI::Application::try_create(arguments));
+ Config::pledge_domains("CharacterMap");
+
+ TRY(Core::System::pledge("stdio recvfd sendfd rpath"));
+ TRY(Core::System::unveil("/res", "r"));
+ TRY(Core::System::unveil(nullptr, nullptr));
+
+ String query;
+ Core::ArgsParser args_parser;
+ args_parser.add_option(query, "Search character names using this query, and print them as a list.", "search", 's', "query");
+ args_parser.parse(arguments);
+
+ if (!query.is_empty()) {
+ search_and_print_results(query);
+ return 0;
+ }
+
+ auto app_icon = TRY(GUI::Icon::try_create_default_icon("app-keyboard-settings"));
+ auto window = TRY(GUI::Window::try_create());
+ window->set_title("Character Map");
+ window->set_icon(app_icon.bitmap_for_size(16));
+ window->resize(400, 400);
+
+ auto character_map_widget = TRY(window->try_set_main_widget<CharacterMapWidget>());
+ character_map_widget->initialize_menubar(*window);
+
+ auto font_query = Config::read_string("CharacterMap", "History", "Font", Gfx::FontDatabase::the().default_font_query());
+ character_map_widget->set_font(Gfx::FontDatabase::the().get_by_name(font_query));
+
+ window->show();
+ return app->exec();
+}