CharacterMapWidget.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /*
  2. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  3. * Copyright (c) 2022, the SerenityOS developers.
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "CharacterMapWidget.h"
  8. #include "CharacterSearchWidget.h"
  9. #include <AK/StringUtils.h>
  10. #include <Applications/CharacterMap/CharacterMapWindowGML.h>
  11. #include <LibConfig/Client.h>
  12. #include <LibDesktop/Launcher.h>
  13. #include <LibGUI/Action.h>
  14. #include <LibGUI/Application.h>
  15. #include <LibGUI/Clipboard.h>
  16. #include <LibGUI/FontPicker.h>
  17. #include <LibGUI/Icon.h>
  18. #include <LibGUI/InputBox.h>
  19. #include <LibGUI/ItemListModel.h>
  20. #include <LibGUI/Label.h>
  21. #include <LibGUI/ListView.h>
  22. #include <LibGUI/Menu.h>
  23. #include <LibGUI/TextBox.h>
  24. #include <LibGUI/Toolbar.h>
  25. #include <LibUnicode/CharacterTypes.h>
  26. CharacterMapWidget::CharacterMapWidget()
  27. {
  28. load_from_gml(character_map_window_gml);
  29. m_toolbar = find_descendant_of_type_named<GUI::Toolbar>("toolbar");
  30. m_font_name_label = find_descendant_of_type_named<GUI::Label>("font_name");
  31. m_glyph_map = find_descendant_of_type_named<GUI::GlyphMapWidget>("glyph_map");
  32. m_output_box = find_descendant_of_type_named<GUI::TextBox>("output_box");
  33. m_copy_output_button = find_descendant_of_type_named<GUI::Button>("copy_output_button");
  34. m_statusbar = find_descendant_of_type_named<GUI::Statusbar>("statusbar");
  35. m_unicode_block_listview = find_descendant_of_type_named<GUI::ListView>("unicode_block_listview");
  36. 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&) {
  37. auto font_picker = GUI::FontPicker::construct(window(), &font(), false);
  38. if (font_picker->exec() == GUI::Dialog::ExecResult::OK) {
  39. auto& font = *font_picker->font();
  40. Config::write_string("CharacterMap", "History", "Font", font.qualified_name());
  41. set_font(font);
  42. }
  43. });
  44. m_copy_selection_action = GUI::CommonActions::make_copy_action([&](GUI::Action&) {
  45. auto selection = m_glyph_map->selection();
  46. StringBuilder builder;
  47. for (auto code_point = selection.start(); code_point < selection.start() + selection.size(); ++code_point) {
  48. if (!m_glyph_map->font().contains_glyph(code_point))
  49. continue;
  50. builder.append_code_point(code_point);
  51. }
  52. GUI::Clipboard::the().set_plain_text(builder.to_string());
  53. });
  54. m_copy_selection_action->set_status_tip("Copy the highlighted characters to the clipboard");
  55. 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&) {
  56. m_glyph_map->select_previous_existing_glyph();
  57. });
  58. m_previous_glyph_action->set_status_tip("Seek the previous visible glyph");
  59. 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&) {
  60. m_glyph_map->select_next_existing_glyph();
  61. });
  62. m_next_glyph_action->set_status_tip("Seek the next visible glyph");
  63. 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&) {
  64. String input;
  65. if (GUI::InputBox::show(window(), input, "Hexadecimal:", "Go to glyph") == GUI::InputBox::ExecResult::OK && !input.is_empty()) {
  66. auto maybe_code_point = AK::StringUtils::convert_to_uint_from_hex(input);
  67. if (!maybe_code_point.has_value())
  68. return;
  69. auto code_point = maybe_code_point.value();
  70. code_point = clamp(code_point, m_range.first, m_range.last);
  71. m_glyph_map->set_focus(true);
  72. m_glyph_map->set_active_glyph(code_point);
  73. m_glyph_map->scroll_to_glyph(code_point);
  74. }
  75. });
  76. m_go_to_glyph_action->set_status_tip("Go to the specified code point");
  77. 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&) {
  78. if (m_find_window.is_null()) {
  79. m_find_window = GUI::Window::construct(window());
  80. auto& search_widget = m_find_window->set_main_widget<CharacterSearchWidget>();
  81. search_widget.on_character_selected = [&](auto code_point) {
  82. m_glyph_map->set_active_glyph(code_point);
  83. m_glyph_map->scroll_to_glyph(code_point);
  84. };
  85. m_find_window->set_icon(GUI::Icon::try_create_default_icon("find").value().bitmap_for_size(16));
  86. m_find_window->set_title("Find a character");
  87. m_find_window->resize(300, 400);
  88. }
  89. m_find_window->show();
  90. m_find_window->move_to_front();
  91. m_find_window->find_descendant_of_type_named<GUI::TextBox>("search_input")->set_focus(true);
  92. });
  93. m_toolbar->add_action(*m_choose_font_action);
  94. m_toolbar->add_separator();
  95. m_toolbar->add_action(*m_copy_selection_action);
  96. m_toolbar->add_separator();
  97. m_toolbar->add_action(*m_previous_glyph_action);
  98. m_toolbar->add_action(*m_next_glyph_action);
  99. m_toolbar->add_action(*m_go_to_glyph_action);
  100. m_toolbar->add_action(*m_find_glyphs_action);
  101. m_glyph_map->on_active_glyph_changed = [&](int) {
  102. update_statusbar();
  103. };
  104. m_glyph_map->on_glyph_double_clicked = [&](int code_point) {
  105. StringBuilder builder;
  106. builder.append(m_output_box->text());
  107. builder.append_code_point(code_point);
  108. m_output_box->set_text(builder.string_view());
  109. };
  110. m_copy_output_button->on_click = [&](auto) {
  111. GUI::Clipboard::the().set_plain_text(m_output_box->text());
  112. };
  113. auto unicode_blocks = Unicode::block_display_names();
  114. m_unicode_block_listview->on_selection_change = [this, unicode_blocks] {
  115. auto index = m_unicode_block_listview->selection().first();
  116. if (index.row() > 0)
  117. m_range = unicode_blocks[index.row() - 1].code_point_range;
  118. else
  119. m_range = { 0x0000, 0x10FFFF };
  120. m_glyph_map->set_active_range(m_range);
  121. };
  122. m_unicode_block_list.append("Show All");
  123. for (auto& block : unicode_blocks)
  124. m_unicode_block_list.append(block.display_name);
  125. m_unicode_block_model = GUI::ItemListModel<String>::create(m_unicode_block_list);
  126. m_unicode_block_listview->set_model(*m_unicode_block_model);
  127. m_unicode_block_listview->set_activates_on_selection(true);
  128. m_unicode_block_listview->horizontal_scrollbar().set_visible(false);
  129. m_unicode_block_listview->set_cursor(m_unicode_block_model->index(0, 0), GUI::AbstractView::SelectionUpdate::Set);
  130. did_change_font();
  131. update_statusbar();
  132. }
  133. void CharacterMapWidget::initialize_menubar(GUI::Window& window)
  134. {
  135. auto& file_menu = window.add_menu("&File");
  136. file_menu.add_action(GUI::CommonActions::make_quit_action([](GUI::Action&) {
  137. GUI::Application::the()->quit();
  138. }));
  139. auto& help_menu = window.add_menu("&Help");
  140. help_menu.add_action(GUI::CommonActions::make_help_action([&](auto&) {
  141. Desktop::Launcher::open(URL::create_with_file_protocol("/usr/share/man/man1/CharacterMap.md"), "/bin/Help");
  142. }));
  143. help_menu.add_action(GUI::CommonActions::make_about_action("Character Map", GUI::Icon::default_icon("app-character-map"), &window));
  144. }
  145. void CharacterMapWidget::did_change_font()
  146. {
  147. m_glyph_map->set_font(font());
  148. m_font_name_label->set_text(font().human_readable_name());
  149. m_output_box->set_font(font());
  150. }
  151. void CharacterMapWidget::update_statusbar()
  152. {
  153. auto code_point = m_glyph_map->active_glyph();
  154. StringBuilder builder;
  155. builder.appendff("U+{:04X}", code_point);
  156. if (auto display_name = Unicode::code_point_display_name(code_point); display_name.has_value())
  157. builder.appendff(" - {}", display_name.value());
  158. m_statusbar->set_text(builder.to_string());
  159. }