mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Add search field in keyboard shortcuts dialog (fix #849)
Changes: * Added "icon_search" part in the skin * Added app::SearchEntry widget * Fixed Separator widget to handle a custom background color, because now we use Separators inside a ListBox too * Added Entry::(on)getEntryTextBounds() to specify a customized area to show text (as SearchEntry needs space for search and close icons)
This commit is contained in:
		
							parent
							
								
									d5d1ac0d47
								
							
						
					
					
						commit
						771a7ba467
					
				
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB | 
|  | @ -407,7 +407,8 @@ | ||||||
|     <part id="horizontal_symmetry"              x="160" y="240" w="13" h="13" /> |     <part id="horizontal_symmetry"              x="160" y="240" w="13" h="13" /> | ||||||
|     <part id="vertical_symmetry"                x="176" y="240" w="13" h="13" /> |     <part id="vertical_symmetry"                x="176" y="240" w="13" h="13" /> | ||||||
|     <part id="icon_arrow_down"                  x="144" y="256" w="7" h="4" /> |     <part id="icon_arrow_down"                  x="144" y="256" w="7" h="4" /> | ||||||
|     <part id="icon_close"                       x="153" y="256" w="7" h="7" /> |     <part id="icon_close"                       x="152" y="256" w="7" h="7" /> | ||||||
|  |     <part id="icon_search"                      x="160" y="256" w="8" h="8" /> | ||||||
|   </parts> |   </parts> | ||||||
| 
 | 
 | ||||||
|   <stylesheet> |   <stylesheet> | ||||||
|  |  | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
|     <vbox> |     <vbox> | ||||||
|       <hbox expansive="true"> |       <hbox expansive="true"> | ||||||
|         <vbox> |         <vbox> | ||||||
|  |           <search id="search" magnet="true" /> | ||||||
|           <view width="80" expansive="true"> |           <view width="80" expansive="true"> | ||||||
|             <listbox id="section" expansive="true" /> |             <listbox id="section" expansive="true" /> | ||||||
|           </view> |           </view> | ||||||
|  | @ -14,6 +15,9 @@ | ||||||
|           <button text="&Reset" id="reset_button" /> |           <button text="&Reset" id="reset_button" /> | ||||||
|         </vbox> |         </vbox> | ||||||
|         <vbox expansive="true"> |         <vbox expansive="true"> | ||||||
|  |           <view id="search_view" expansive="true"> | ||||||
|  |             <listbox id="search_list" /> | ||||||
|  |           </view> | ||||||
|           <view id="menus_view" expansive="true"> |           <view id="menus_view" expansive="true"> | ||||||
|             <listbox id="menus" /> |             <listbox id="menus" /> | ||||||
|           </view> |           </view> | ||||||
|  |  | ||||||
|  | @ -381,6 +381,7 @@ add_library(app-lib | ||||||
|   ui/preview_editor.cpp |   ui/preview_editor.cpp | ||||||
|   ui/recent_listbox.cpp |   ui/recent_listbox.cpp | ||||||
|   ui/resources_listbox.cpp |   ui/resources_listbox.cpp | ||||||
|  |   ui/search_entry.cpp | ||||||
|   ui/select_accelerator.cpp |   ui/select_accelerator.cpp | ||||||
|   ui/skin/button_icon_impl.cpp |   ui/skin/button_icon_impl.cpp | ||||||
|   ui/skin/skin_part.cpp |   ui/skin/skin_part.cpp | ||||||
|  |  | ||||||
|  | @ -18,16 +18,21 @@ | ||||||
| #include "app/tools/tool.h" | #include "app/tools/tool.h" | ||||||
| #include "app/ui/app_menuitem.h" | #include "app/ui/app_menuitem.h" | ||||||
| #include "app/ui/keyboard_shortcuts.h" | #include "app/ui/keyboard_shortcuts.h" | ||||||
|  | #include "app/ui/search_entry.h" | ||||||
| #include "app/ui/select_accelerator.h" | #include "app/ui/select_accelerator.h" | ||||||
| #include "app/ui/skin/skin_theme.h" | #include "app/ui/skin/skin_theme.h" | ||||||
| #include "base/bind.h" | #include "base/bind.h" | ||||||
| #include "base/fs.h" | #include "base/fs.h" | ||||||
| #include "base/path.h" | #include "base/path.h" | ||||||
|  | #include "base/scoped_value.h" | ||||||
|  | #include "base/split_string.h" | ||||||
|  | #include "base/string.h" | ||||||
| #include "ui/graphics.h" | #include "ui/graphics.h" | ||||||
| #include "ui/listitem.h" | #include "ui/listitem.h" | ||||||
| #include "ui/paint_event.h" | #include "ui/paint_event.h" | ||||||
| #include "ui/preferred_size_event.h" | #include "ui/preferred_size_event.h" | ||||||
| #include "ui/resize_event.h" | #include "ui/resize_event.h" | ||||||
|  | #include "ui/separator.h" | ||||||
| 
 | 
 | ||||||
| #include "keyboard_shortcuts.xml.h" | #include "keyboard_shortcuts.xml.h" | ||||||
| 
 | 
 | ||||||
|  | @ -55,6 +60,8 @@ public: | ||||||
|     setBorder(border); |     setBorder(border); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   Key* key() { return m_key; } | ||||||
|  | 
 | ||||||
|   void restoreKeys() { |   void restoreKeys() { | ||||||
|     if (m_key && m_keyOrig) |     if (m_key && m_keyOrig) | ||||||
|       *m_key = *m_keyOrig; |       *m_key = *m_keyOrig; | ||||||
|  | @ -280,7 +287,7 @@ private: | ||||||
| 
 | 
 | ||||||
| class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts { | class KeyboardShortcutsWindow : public app::gen::KeyboardShortcuts { | ||||||
| public: | public: | ||||||
|   KeyboardShortcutsWindow() { |   KeyboardShortcutsWindow() : m_searchChange(false) { | ||||||
|     setAutoRemap(false); |     setAutoRemap(false); | ||||||
| 
 | 
 | ||||||
|     section()->addChild(new ListItem("Menus")); |     section()->addChild(new ListItem("Menus")); | ||||||
|  | @ -288,6 +295,7 @@ public: | ||||||
|     section()->addChild(new ListItem("Tools")); |     section()->addChild(new ListItem("Tools")); | ||||||
|     section()->addChild(new ListItem("Action Modifiers")); |     section()->addChild(new ListItem("Action Modifiers")); | ||||||
| 
 | 
 | ||||||
|  |     search()->Change.connect(Bind<void>(&KeyboardShortcutsWindow::onSearchChange, this)); | ||||||
|     section()->Change.connect(Bind<void>(&KeyboardShortcutsWindow::onSectionChange, this)); |     section()->Change.connect(Bind<void>(&KeyboardShortcutsWindow::onSectionChange, this)); | ||||||
|     importButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onImport, this)); |     importButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onImport, this)); | ||||||
|     exportButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onExport, this)); |     exportButton()->Click.connect(Bind<void>(&KeyboardShortcutsWindow::onExport, this)); | ||||||
|  | @ -304,6 +312,8 @@ public: | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
|   void deleteAllKeyItems() { |   void deleteAllKeyItems() { | ||||||
|  |     while (searchList()->getLastChild()) | ||||||
|  |       searchList()->removeChild(searchList()->getLastChild()); | ||||||
|     while (menus()->getLastChild()) |     while (menus()->getLastChild()) | ||||||
|       menus()->removeChild(menus()->getLastChild()); |       menus()->removeChild(menus()->getLastChild()); | ||||||
|     while (commands()->getLastChild()) |     while (commands()->getLastChild()) | ||||||
|  | @ -375,12 +385,78 @@ private: | ||||||
|     this->actions()->sortItems(); |     this->actions()->sortItems(); | ||||||
| 
 | 
 | ||||||
|     section()->selectIndex(0); |     section()->selectIndex(0); | ||||||
|     onSectionChange(); |     updateViews(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void fillSearchList(const std::string& search) { | ||||||
|  |     while (searchList()->getLastChild()) | ||||||
|  |       searchList()->removeChild(searchList()->getLastChild()); | ||||||
|  | 
 | ||||||
|  |     std::vector<std::string> parts; | ||||||
|  |     base::split_string(base::string_to_lower(search), parts, " "); | ||||||
|  | 
 | ||||||
|  |     ListBox* listBoxes[] = { commands(), tools(), actions() }; | ||||||
|  |     int sectionIdx = 1;         // index 0 is menus, index 1 is commands
 | ||||||
|  |     for (auto listBox : listBoxes) { | ||||||
|  |       Separator* group = nullptr; | ||||||
|  | 
 | ||||||
|  |       for (auto item : listBox->getChildren()) { | ||||||
|  |         if (KeyItem* keyItem = dynamic_cast<KeyItem*>(item)) { | ||||||
|  |           std::string itemText = | ||||||
|  |             base::string_to_lower(keyItem->getText()); | ||||||
|  |           int matches = 0; | ||||||
|  | 
 | ||||||
|  |           for (const auto& part : parts) { | ||||||
|  |             if (itemText.find(part) != std::string::npos) | ||||||
|  |               ++matches; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           if (matches == int(parts.size())) { | ||||||
|  |             if (!group) { | ||||||
|  |               group = new Separator( | ||||||
|  |                 section()->getChildren()[sectionIdx]->getText(), HORIZONTAL); | ||||||
|  |               group->setBgColor(SkinTheme::instance()->colors.background()); | ||||||
|  | 
 | ||||||
|  |               searchList()->addChild(group); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             KeyItem* copyItem = | ||||||
|  |               new KeyItem(keyItem->getText(), | ||||||
|  |                           keyItem->key(), nullptr, 0); | ||||||
|  |             searchList()->addChild(copyItem); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ++sectionIdx; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void onSearchChange() { | ||||||
|  |     base::ScopedValue<bool> flag(m_searchChange, true, false); | ||||||
|  |     std::string searchText = search()->getText(); | ||||||
|  | 
 | ||||||
|  |     if (searchText.empty()) | ||||||
|  |       section()->selectIndex(0); | ||||||
|  |     else { | ||||||
|  |       fillSearchList(searchText); | ||||||
|  |       section()->selectChild(nullptr); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     updateViews(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   void onSectionChange() { |   void onSectionChange() { | ||||||
|     int section = this->section()->getSelectedIndex(); |     if (m_searchChange) | ||||||
|  |       return; | ||||||
| 
 | 
 | ||||||
|  |     search()->setText(""); | ||||||
|  |     updateViews(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void updateViews() { | ||||||
|  |     int section = this->section()->getSelectedIndex(); | ||||||
|  |     searchView()->setVisible(section < 0); | ||||||
|     menusView()->setVisible(section == 0); |     menusView()->setVisible(section == 0); | ||||||
|     commandsView()->setVisible(section == 1); |     commandsView()->setVisible(section == 1); | ||||||
|     toolsView()->setVisible(section == 2); |     toolsView()->setVisible(section == 2); | ||||||
|  | @ -438,6 +514,7 @@ private: | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   std::vector<KeyItem*> m_allKeyItems; |   std::vector<KeyItem*> m_allKeyItems; | ||||||
|  |   bool m_searchChange; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| class KeyboardShortcutsCommand : public Command { | class KeyboardShortcutsCommand : public Command { | ||||||
|  |  | ||||||
|  | @ -0,0 +1,107 @@ | ||||||
|  | // Aseprite
 | ||||||
|  | // Copyright (C) 2001-2015  David Capello
 | ||||||
|  | //
 | ||||||
|  | // This program is free software; you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License version 2 as
 | ||||||
|  | // published by the Free Software Foundation.
 | ||||||
|  | 
 | ||||||
|  | #ifdef HAVE_CONFIG_H | ||||||
|  | #include "config.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #include "app/ui/search_entry.h" | ||||||
|  | 
 | ||||||
|  | #include "app/ui/skin/skin_theme.h" | ||||||
|  | #include "she/surface.h" | ||||||
|  | #include "ui/graphics.h" | ||||||
|  | #include "ui/message.h" | ||||||
|  | #include "ui/paint_event.h" | ||||||
|  | #include "ui/preferred_size_event.h" | ||||||
|  | 
 | ||||||
|  | namespace app { | ||||||
|  | 
 | ||||||
|  | using namespace app::skin; | ||||||
|  | using namespace gfx; | ||||||
|  | using namespace ui; | ||||||
|  | 
 | ||||||
|  | SearchEntry::SearchEntry() | ||||||
|  |   : Entry(256, "") | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool SearchEntry::onProcessMessage(ui::Message* msg) | ||||||
|  | { | ||||||
|  |   switch (msg->type()) { | ||||||
|  |     case kMouseDownMessage: { | ||||||
|  |       Rect closeBounds = getCloseIconBounds(); | ||||||
|  |       Point mousePos = static_cast<MouseMessage*>(msg)->position() | ||||||
|  |         - getBounds().getOrigin(); | ||||||
|  | 
 | ||||||
|  |       if (closeBounds.contains(mousePos)) { | ||||||
|  |         setText(""); | ||||||
|  |         onChange(); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return Entry::onProcessMessage(msg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SearchEntry::onPaint(ui::PaintEvent& ev) | ||||||
|  | { | ||||||
|  |   SkinTheme* theme = static_cast<SkinTheme*>(getTheme()); | ||||||
|  |   theme->paintEntry(ev); | ||||||
|  | 
 | ||||||
|  |   auto icon = theme->parts.iconSearch()->getBitmap(0); | ||||||
|  |   Rect bounds = getClientBounds(); | ||||||
|  |   ev.getGraphics()->drawColoredRgbaSurface( | ||||||
|  |     icon, theme->colors.text(), | ||||||
|  |     bounds.x + border().left(), | ||||||
|  |     bounds.y + bounds.h/2 - icon->height()/2); | ||||||
|  | 
 | ||||||
|  |   if (!getText().empty()) { | ||||||
|  |     icon = theme->parts.iconClose()->getBitmap(0); | ||||||
|  |     ev.getGraphics()->drawColoredRgbaSurface( | ||||||
|  |       icon, theme->colors.text(), | ||||||
|  |       bounds.x + bounds.w - border().right() - childSpacing() - icon->width(), | ||||||
|  |       bounds.y + bounds.h/2 - icon->height()/2); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void SearchEntry::onPreferredSize(PreferredSizeEvent& ev) | ||||||
|  | { | ||||||
|  |   Entry::onPreferredSize(ev); | ||||||
|  |   Size sz = ev.getPreferredSize(); | ||||||
|  | 
 | ||||||
|  |   SkinTheme* theme = static_cast<SkinTheme*>(getTheme()); | ||||||
|  |   auto icon = theme->parts.iconSearch()->getBitmap(0); | ||||||
|  |   sz.h = MAX(sz.h, icon->height()+border().height()); | ||||||
|  | 
 | ||||||
|  |   ev.setPreferredSize(sz); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Rect SearchEntry::onGetEntryTextBounds() const | ||||||
|  | { | ||||||
|  |   SkinTheme* theme = static_cast<SkinTheme*>(getTheme()); | ||||||
|  |   Rect bounds = Entry::onGetEntryTextBounds(); | ||||||
|  |   auto icon1 = theme->parts.iconSearch()->getBitmap(0); | ||||||
|  |   auto icon2 = theme->parts.iconClose()->getBitmap(0); | ||||||
|  |   bounds.x += childSpacing() + icon1->width(); | ||||||
|  |   bounds.w -= 2*childSpacing() + icon1->width() + icon2->width(); | ||||||
|  |   return bounds; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Rect SearchEntry::getCloseIconBounds() const | ||||||
|  | { | ||||||
|  |   SkinTheme* theme = static_cast<SkinTheme*>(getTheme()); | ||||||
|  |   Rect bounds = getClientBounds(); | ||||||
|  |   auto icon = theme->parts.iconClose()->getBitmap(0); | ||||||
|  |   bounds.x += bounds.w - border().right() - childSpacing() - icon->width(); | ||||||
|  |   bounds.y += bounds.h/2 - icon->height()/2; | ||||||
|  |   bounds.w = icon->width(); | ||||||
|  |   bounds.h = icon->height(); | ||||||
|  |   return bounds; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace app
 | ||||||
|  | @ -0,0 +1,31 @@ | ||||||
|  | // Aseprite
 | ||||||
|  | // Copyright (C) 2001-2015  David Capello
 | ||||||
|  | //
 | ||||||
|  | // This program is free software; you can redistribute it and/or modify
 | ||||||
|  | // it under the terms of the GNU General Public License version 2 as
 | ||||||
|  | // published by the Free Software Foundation.
 | ||||||
|  | 
 | ||||||
|  | #ifndef APP_UI_SEARCH_ENTRY_H_INCLUDED | ||||||
|  | #define APP_UI_SEARCH_ENTRY_H_INCLUDED | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include "ui/entry.h" | ||||||
|  | 
 | ||||||
|  | namespace app { | ||||||
|  | 
 | ||||||
|  |   class SearchEntry : public ui::Entry { | ||||||
|  |   public: | ||||||
|  |     SearchEntry(); | ||||||
|  | 
 | ||||||
|  |   private: | ||||||
|  |     bool onProcessMessage(ui::Message* msg) override; | ||||||
|  |     void onPaint(ui::PaintEvent& ev) override; | ||||||
|  |     void onPreferredSize(ui::PreferredSizeEvent& ev) override; | ||||||
|  |     gfx::Rect onGetEntryTextBounds() const override; | ||||||
|  | 
 | ||||||
|  |     gfx::Rect getCloseIconBounds() const; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  | } // namespace app
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
|  | @ -568,6 +568,7 @@ void SkinTheme::initWidget(Widget* widget) | ||||||
|         parts.sunkenNormal()->getBitmapN()->height(), |         parts.sunkenNormal()->getBitmapN()->height(), | ||||||
|         parts.sunkenNormal()->getBitmapE()->width(), |         parts.sunkenNormal()->getBitmapE()->width(), | ||||||
|         parts.sunkenNormal()->getBitmapS()->height()); |         parts.sunkenNormal()->getBitmapS()->height()); | ||||||
|  |       widget->setChildSpacing(3 * scale); | ||||||
|       break; |       break; | ||||||
| 
 | 
 | ||||||
|     case kGridWidget: |     case kGridWidget: | ||||||
|  | @ -650,17 +651,6 @@ void SkinTheme::initWidget(Widget* widget) | ||||||
|       else { |       else { | ||||||
|         BORDER4(4 * scale, 2 * scale, 1 * scale, 2 * scale); |         BORDER4(4 * scale, 2 * scale, 1 * scale, 2 * scale); | ||||||
|       } |       } | ||||||
| 
 |  | ||||||
|       if (widget->hasText()) { |  | ||||||
|         gfx::Border border = widget->border(); |  | ||||||
| 
 |  | ||||||
|         if (widget->getAlign() & TOP) |  | ||||||
|           border.top(widget->getTextHeight()); |  | ||||||
|         else if (widget->getAlign() & BOTTOM) |  | ||||||
|           border.bottom(widget->getTextHeight()); |  | ||||||
| 
 |  | ||||||
|         widget->setBorder(border); |  | ||||||
|       } |  | ||||||
|       break; |       break; | ||||||
| 
 | 
 | ||||||
|     case kSliderWidget: |     case kSliderWidget: | ||||||
|  | @ -923,8 +913,9 @@ void SkinTheme::paintEntry(PaintEvent& ev) | ||||||
|     bg); |     bg); | ||||||
| 
 | 
 | ||||||
|   // Draw the text
 |   // Draw the text
 | ||||||
|   x = bounds.x + widget->border().left(); |   bounds = widget->getEntryTextBounds(); | ||||||
|   y = bounds.y + bounds.h/2 - widget->getTextHeight()/2; |   x = bounds.x; | ||||||
|  |   y = bounds.y; | ||||||
| 
 | 
 | ||||||
|   base::utf8_const_iterator utf8_it = base::utf8_const_iterator(textString.begin()); |   base::utf8_const_iterator utf8_it = base::utf8_const_iterator(textString.begin()); | ||||||
|   int textlen = base::utf8_length(textString); |   int textlen = base::utf8_length(textString); | ||||||
|  | @ -954,7 +945,7 @@ void SkinTheme::paintEntry(PaintEvent& ev) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     w = g->measureChar(ch).w; |     w = g->measureChar(ch).w; | ||||||
|     if (x+w > bounds.x2()-3) |     if (x+w > bounds.x2()-widget->childSpacing()*guiscale()) | ||||||
|       return; |       return; | ||||||
| 
 | 
 | ||||||
|     caret_x = x; |     caret_x = x; | ||||||
|  | @ -969,7 +960,7 @@ void SkinTheme::paintEntry(PaintEvent& ev) | ||||||
|   // Draw suffix if there is enough space
 |   // Draw suffix if there is enough space
 | ||||||
|   if (!widget->getSuffix().empty()) { |   if (!widget->getSuffix().empty()) { | ||||||
|     Rect sufBounds(x, y, |     Rect sufBounds(x, y, | ||||||
|                    bounds.x2()-3*guiscale()-x, |                    bounds.x2()-widget->childSpacing()*guiscale()-x, | ||||||
|                    widget->getTextHeight()); |                    widget->getTextHeight()); | ||||||
|     IntersectClip clip(g, sufBounds); |     IntersectClip clip(g, sufBounds); | ||||||
|     if (clip) { |     if (clip) { | ||||||
|  | @ -1228,22 +1219,27 @@ void SkinTheme::paintSeparator(ui::PaintEvent& ev) | ||||||
|   // background
 |   // background
 | ||||||
|   g->fillRect(BGCOLOR, bounds); |   g->fillRect(BGCOLOR, bounds); | ||||||
| 
 | 
 | ||||||
|   if (widget->getAlign() & HORIZONTAL) |   if (widget->getAlign() & HORIZONTAL) { | ||||||
|     drawHline(g, bounds, parts.separatorHorz().get()); |     int h = parts.separatorHorz()->getBitmap(0)->height(); | ||||||
|  |     drawHline(g, gfx::Rect(bounds.x, bounds.y+bounds.h/2-h/2, | ||||||
|  |                            bounds.w, h), | ||||||
|  |               parts.separatorHorz().get()); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   if (widget->getAlign() & VERTICAL) |   if (widget->getAlign() & VERTICAL) { | ||||||
|     drawVline(g, bounds, parts.separatorVert().get()); |     int w = parts.separatorVert()->getBitmap(0)->width(); | ||||||
|  |     drawVline(g, gfx::Rect(bounds.x+bounds.w/2-w/2, bounds.y, | ||||||
|  |                            w, bounds.h), | ||||||
|  |               parts.separatorVert().get()); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   // text
 |   // text
 | ||||||
|   if (widget->hasText()) { |   if (widget->hasText()) { | ||||||
|     int h = widget->getTextHeight(); |     int h = widget->getTextHeight(); | ||||||
|     Rect r( |     Rect r( | ||||||
|       Point( |       bounds.x + widget->border().left()/2 + h/2, | ||||||
|         bounds.x + widget->border().left()/2 + h/2, |       bounds.y + bounds.h/2 - h/2, | ||||||
|         bounds.y + widget->border().top()/2 - h/2), |       widget->getTextWidth(), h); | ||||||
|       Point( |  | ||||||
|         bounds.x2() - widget->border().right()/2 - h, |  | ||||||
|         bounds.y2() - widget->border().bottom()/2 + h)); |  | ||||||
| 
 | 
 | ||||||
|     drawTextString(g, NULL, |     drawTextString(g, NULL, | ||||||
|       colors.separatorLabel(), BGCOLOR, |       colors.separatorLabel(), BGCOLOR, | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| #include "app/ui/button_set.h" | #include "app/ui/button_set.h" | ||||||
| #include "app/ui/color_button.h" | #include "app/ui/color_button.h" | ||||||
| #include "app/ui/drop_down_button.h" | #include "app/ui/drop_down_button.h" | ||||||
|  | #include "app/ui/search_entry.h" | ||||||
| #include "app/ui/skin/skin_style_property.h" | #include "app/ui/skin/skin_style_property.h" | ||||||
| #include "app/ui/skin/skin_theme.h" | #include "app/ui/skin/skin_theme.h" | ||||||
| #include "app/widget_not_found.h" | #include "app/widget_not_found.h" | ||||||
|  | @ -466,6 +467,10 @@ Widget* WidgetLoader::convertXmlElementToWidget(const TiXmlElement* elem, Widget | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |   else if (elem_name == "search") { | ||||||
|  |     if (!widget) | ||||||
|  |       widget = new SearchEntry; | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   // Was the widget created?
 |   // Was the widget created?
 | ||||||
|   if (widget) |   if (widget) | ||||||
|  |  | ||||||
|  | @ -64,6 +64,7 @@ static std::string convert_type(const std::string& name) | ||||||
|   if (name == "listbox") return "ui::ListBox"; |   if (name == "listbox") return "ui::ListBox"; | ||||||
|   if (name == "panel") return "ui::Panel"; |   if (name == "panel") return "ui::Panel"; | ||||||
|   if (name == "radio") return "ui::RadioButton"; |   if (name == "radio") return "ui::RadioButton"; | ||||||
|  |   if (name == "search") return "app::SearchEntry"; | ||||||
|   if (name == "slider") return "ui::Slider"; |   if (name == "slider") return "ui::Slider"; | ||||||
|   if (name == "splitter") return "ui::Splitter"; |   if (name == "splitter") return "ui::Splitter"; | ||||||
|   if (name == "vbox") return "ui::VBox"; |   if (name == "vbox") return "ui::VBox"; | ||||||
|  |  | ||||||
|  | @ -178,6 +178,11 @@ void Entry::getEntryThemeInfo(int* scroll, int* caret, int* state, | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | gfx::Rect Entry::getEntryTextBounds() const | ||||||
|  | { | ||||||
|  |   return onGetEntryTextBounds(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool Entry::onProcessMessage(Message* msg) | bool Entry::onProcessMessage(Message* msg) | ||||||
| { | { | ||||||
|   switch (msg->type()) { |   switch (msg->type()) { | ||||||
|  | @ -454,27 +459,37 @@ void Entry::onChange() | ||||||
|   Change(); |   Change(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | gfx::Rect Entry::onGetEntryTextBounds() const | ||||||
|  | { | ||||||
|  |   gfx::Rect bounds = getClientBounds(); | ||||||
|  |   bounds.x += border().left(); | ||||||
|  |   bounds.y += bounds.h/2 - getTextHeight()/2; | ||||||
|  |   bounds.w -= border().width(); | ||||||
|  |   bounds.h = getTextHeight(); | ||||||
|  |   return bounds; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| int Entry::getCaretFromMouse(MouseMessage* mousemsg) | int Entry::getCaretFromMouse(MouseMessage* mousemsg) | ||||||
| { | { | ||||||
|   base::utf8_const_iterator utf8_begin = base::utf8_const_iterator(getText().begin()); |   base::utf8_const_iterator utf8_begin = base::utf8_const_iterator(getText().begin()); | ||||||
|   base::utf8_const_iterator utf8_end = base::utf8_const_iterator(getText().end()); |   base::utf8_const_iterator utf8_end = base::utf8_const_iterator(getText().end()); | ||||||
|   int c, x, w, mx, caret = m_caret; |   int caret = m_caret; | ||||||
|   int textlen = base::utf8_length(getText()); |   int textlen = base::utf8_length(getText()); | ||||||
|  |   gfx::Rect bounds = getEntryTextBounds().offset(getBounds().getOrigin()); | ||||||
| 
 | 
 | ||||||
|   mx = mousemsg->position().x; |   int mx = mousemsg->position().x; | ||||||
|   mx = MID(getBounds().x+border().left(), |   mx = MID(bounds.x, mx, bounds.x2()-1); | ||||||
|            mx, |  | ||||||
|            getBounds().x2()-border().right()-1); |  | ||||||
| 
 | 
 | ||||||
|   x = getBounds().x + border().left(); |   int x = bounds.x; | ||||||
| 
 | 
 | ||||||
|   base::utf8_const_iterator utf8_it = |   base::utf8_const_iterator utf8_it = | ||||||
|     (m_scroll < textlen ? |     (m_scroll < textlen ? | ||||||
|       utf8_begin + m_scroll: |       utf8_begin + m_scroll: | ||||||
|       utf8_end); |       utf8_end); | ||||||
| 
 | 
 | ||||||
|   for (c=m_scroll; utf8_it != utf8_end; ++c, ++utf8_it) { |   int c = m_scroll; | ||||||
|     w = getFont()->charWidth(*utf8_it); |   for (; utf8_it != utf8_end; ++c, ++utf8_it) { | ||||||
|  |     int w = getFont()->charWidth(*utf8_it); | ||||||
|     if (x+w >= getBounds().x2()-border().right()) |     if (x+w >= getBounds().x2()-border().right()) | ||||||
|       break; |       break; | ||||||
|     if ((mx >= x) && (mx < x+w)) { |     if ((mx >= x) && (mx < x+w)) { | ||||||
|  | @ -485,8 +500,7 @@ int Entry::getCaretFromMouse(MouseMessage* mousemsg) | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (utf8_it == utf8_end) { |   if (utf8_it == utf8_end) { | ||||||
|     if ((mx >= x) && |     if ((mx >= x) && (mx < bounds.x2())) { | ||||||
|         (mx <= getBounds().x2()-border().right()-1)) { |  | ||||||
|       caret = c; |       caret = c; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ namespace ui { | ||||||
|     // for themes
 |     // for themes
 | ||||||
|     void getEntryThemeInfo(int* scroll, int* caret, int* state, |     void getEntryThemeInfo(int* scroll, int* caret, int* state, | ||||||
|                            int* selbeg, int* selend); |                            int* selbeg, int* selend); | ||||||
|  |     gfx::Rect getEntryTextBounds() const; | ||||||
| 
 | 
 | ||||||
|     // Signals
 |     // Signals
 | ||||||
|     Signal0<void> Change; |     Signal0<void> Change; | ||||||
|  | @ -53,6 +54,7 @@ namespace ui { | ||||||
| 
 | 
 | ||||||
|     // New Events
 |     // New Events
 | ||||||
|     virtual void onChange(); |     virtual void onChange(); | ||||||
|  |     virtual gfx::Rect onGetEntryTextBounds() const; | ||||||
| 
 | 
 | ||||||
|   private: |   private: | ||||||
|     enum class EntryCmd { |     enum class EntryCmd { | ||||||
|  |  | ||||||
|  | @ -45,8 +45,10 @@ void Separator::onPreferredSize(PreferredSizeEvent& ev) | ||||||
|     maxSize.h = MAX(maxSize.h, reqSize.h); |     maxSize.h = MAX(maxSize.h, reqSize.h); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   if (hasText()) |   if (hasText()) { | ||||||
|     maxSize.w = MAX(maxSize.w, getTextWidth()); |     maxSize.w = MAX(maxSize.w, getTextWidth()); | ||||||
|  |     maxSize.h = MAX(maxSize.h, getTextHeight()); | ||||||
|  |   } | ||||||
| 
 | 
 | ||||||
|   int w = maxSize.w + border().width(); |   int w = maxSize.w + border().width(); | ||||||
|   int h = maxSize.h + border().height(); |   int h = maxSize.h + border().height(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue