diff options
author | Sam Atkins <atkinssj@serenityos.org> | 2022-01-10 15:59:42 +0000 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-16 11:17:03 +0100 |
commit | 2327ea897071a51322c42fc17ba0184ab49d4546 (patch) | |
tree | ee5d6ab563141eeeab993376082a76db02fceda6 /Userland/Applications | |
parent | 1d79e8cb0f3a27204921c5fe0ac022ec9d76b43f (diff) | |
download | serenity-2327ea897071a51322c42fc17ba0184ab49d4546.zip |
CharacterMap: Add new Character Map application :^)
Diffstat (limited to 'Userland/Applications')
-rw-r--r-- | Userland/Applications/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Applications/CharacterMap/CMakeLists.txt | 17 | ||||
-rw-r--r-- | Userland/Applications/CharacterMap/CharacterMapWidget.cpp | 90 | ||||
-rw-r--r-- | Userland/Applications/CharacterMap/CharacterMapWidget.h | 34 | ||||
-rw-r--r-- | Userland/Applications/CharacterMap/CharacterMapWindow.gml | 44 | ||||
-rw-r--r-- | Userland/Applications/CharacterMap/main.cpp | 88 |
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(); +} |