From f4ff8b17f75e6cd57ca16c665027e7b3f59951b9 Mon Sep 17 00:00:00 2001 From: Francois Ferrand Date: Mon, 29 Apr 2013 18:04:26 +0200 Subject: [PATCH] Use search field for search. Search options are presented in a context menu on the search field, as well as links in search header. --- src/CMakeLists.txt | 2 + src/gui/DatabaseTabWidget.cpp | 8 +- src/gui/DatabaseWidget.cpp | 150 ++++++---- src/gui/DatabaseWidget.h | 19 +- src/gui/MainWindow.cpp | 59 +++- src/gui/MainWindow.h | 1 + src/gui/MainWindow.ui | 70 ++++- src/gui/SearchWidget.ui | 69 +---- src/gui/qocoa/CMakeLists.txt | 50 ++++ src/gui/qocoa/LICENSE.txt | 19 ++ src/gui/qocoa/Qocoa.pro | 17 ++ src/gui/qocoa/README.md | 36 +++ src/gui/qocoa/TODO.md | 13 + src/gui/qocoa/gallery.cpp | 75 +++++ src/gui/qocoa/gallery.h | 14 + src/gui/qocoa/gallery.png | Bin 0 -> 42195 bytes src/gui/qocoa/main.cpp | 12 + src/gui/qocoa/qbutton.h | 49 ++++ src/gui/qocoa/qbutton_mac.mm | 229 +++++++++++++++ src/gui/qocoa/qbutton_nonmac.cpp | 89 ++++++ src/gui/qocoa/qocoa_mac.h | 54 ++++ src/gui/qocoa/qprogressindicatorspinning.h | 29 ++ .../qocoa/qprogressindicatorspinning_mac.mm | 70 +++++ .../qprogressindicatorspinning_nonmac.cpp | 86 ++++++ .../qprogressindicatorspinning_nonmac.gif | Bin 0 -> 3208 bytes .../qprogressindicatorspinning_nonmac.qrc | 5 + src/gui/qocoa/qsearchfield.h | 48 ++++ src/gui/qocoa/qsearchfield_mac.mm | 257 +++++++++++++++++ src/gui/qocoa/qsearchfield_nonmac.cpp | 270 ++++++++++++++++++ src/gui/qocoa/qsearchfield_nonmac.qrc | 7 + src/gui/qocoa/qsearchfield_nonmac_clear.png | Bin 0 -> 736 bytes .../qocoa/qsearchfield_nonmac_magnifier.png | Bin 0 -> 300 bytes .../qsearchfield_nonmac_magnifier_menu.png | Bin 0 -> 439 bytes 33 files changed, 1653 insertions(+), 154 deletions(-) create mode 100644 src/gui/qocoa/CMakeLists.txt create mode 100644 src/gui/qocoa/LICENSE.txt create mode 100644 src/gui/qocoa/Qocoa.pro create mode 100644 src/gui/qocoa/README.md create mode 100644 src/gui/qocoa/TODO.md create mode 100644 src/gui/qocoa/gallery.cpp create mode 100644 src/gui/qocoa/gallery.h create mode 100644 src/gui/qocoa/gallery.png create mode 100644 src/gui/qocoa/main.cpp create mode 100644 src/gui/qocoa/qbutton.h create mode 100644 src/gui/qocoa/qbutton_mac.mm create mode 100644 src/gui/qocoa/qbutton_nonmac.cpp create mode 100644 src/gui/qocoa/qocoa_mac.h create mode 100644 src/gui/qocoa/qprogressindicatorspinning.h create mode 100644 src/gui/qocoa/qprogressindicatorspinning_mac.mm create mode 100644 src/gui/qocoa/qprogressindicatorspinning_nonmac.cpp create mode 100644 src/gui/qocoa/qprogressindicatorspinning_nonmac.gif create mode 100644 src/gui/qocoa/qprogressindicatorspinning_nonmac.qrc create mode 100644 src/gui/qocoa/qsearchfield.h create mode 100644 src/gui/qocoa/qsearchfield_mac.mm create mode 100644 src/gui/qocoa/qsearchfield_nonmac.cpp create mode 100644 src/gui/qocoa/qsearchfield_nonmac.qrc create mode 100644 src/gui/qocoa/qsearchfield_nonmac_clear.png create mode 100644 src/gui/qocoa/qsearchfield_nonmac_magnifier.png create mode 100644 src/gui/qocoa/qsearchfield_nonmac_magnifier_menu.png diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b57ee84d2..6b7ac25b2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -219,12 +219,14 @@ qt4_wrap_cpp(keepassx_SOURCES ${keepassx_MOC}) add_library(keepassx_core STATIC ${keepassx_SOURCES}) set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE) +add_subdirectory(gui/qocoa) add_subdirectory(http/qhttpserver) add_subdirectory(http/qjson) add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE}) target_link_libraries(${PROGNAME} keepassx_core + Qocoa qjson qhttpserver ${QT_QTCORE_LIBRARY} diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 34bf41209..a520fd46d 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -246,12 +246,14 @@ void DatabaseTabWidget::checkReloadDatabases() //Save current group/entry Uuid currentGroup; - if (Group* group = dbStruct.dbWidget->groupView()->currentGroup()) + if (Group* group = dbStruct.dbWidget->currentGroup()) currentGroup = group->uuid(); Uuid currentEntry; if (Entry* entry = dbStruct.dbWidget->entryView()->currentEntry()) currentEntry = entry->uuid(); QString searchText = dbStruct.dbWidget->searchText(); + bool caseSensitive = dbStruct.dbWidget->caseSensitiveSearch(); + bool allGroups = dbStruct.dbWidget->isAllGroupsSearch(); //Reload updated db CompositeKey key = db->key(); @@ -262,11 +264,11 @@ void DatabaseTabWidget::checkReloadDatabases() dbStruct = indexDatabaseManagerStruct(count() - 1); if (dbStruct.dbWidget) { Database * db = dbStruct.dbWidget->database(); - if (!searchText.isEmpty()) - dbStruct.dbWidget->showSearch(searchText); if (!currentGroup.isNull()) if (Group* group = db->resolveGroup(currentGroup)) dbStruct.dbWidget->groupView()->setCurrentGroup(group); + if (!searchText.isEmpty()) + dbStruct.dbWidget->search(searchText, caseSensitive, allGroups); if (!currentEntry.isNull()) if (Entry* entry = db->resolveEntry(currentEntry)) dbStruct.dbWidget->entryView()->setCurrentEntry(entry); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 68b89761f..f368bf945 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -50,6 +50,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) , m_newGroup(Q_NULLPTR) , m_newEntry(Q_NULLPTR) , m_newParent(Q_NULLPTR) + , m_searchAllGroups(false) + , m_searchSensitivity(Qt::CaseInsensitive) { m_searchUi->setupUi(m_searchWidget); @@ -89,8 +91,9 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) closeAction->setIcon(closeIcon); m_searchUi->closeSearchButton->setDefaultAction(closeAction); m_searchUi->closeSearchButton->setShortcut(Qt::Key_Escape); + int iconsize = style()->pixelMetric(QStyle::PM_SmallIconSize); + m_searchUi->closeSearchButton->setIconSize(QSize(iconsize,iconsize)); m_searchWidget->hide(); - m_searchUi->caseSensitiveCheckBox->setVisible(false); QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget); vLayout->setMargin(0); @@ -149,10 +152,7 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent) connect(m_keepass1OpenWidget, SIGNAL(editFinished(bool)), SLOT(openDatabase(bool))); connect(m_unlockDatabaseWidget, SIGNAL(editFinished(bool)), SLOT(unlockDatabase(bool))); connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged())); - connect(m_searchUi->searchEdit, SIGNAL(textChanged(QString)), this, SLOT(startSearchTimer())); - connect(m_searchUi->caseSensitiveCheckBox, SIGNAL(toggled(bool)), this, SLOT(startSearch())); - connect(m_searchUi->searchCurrentRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch())); - connect(m_searchUi->searchRootRadioButton, SIGNAL(toggled(bool)), this, SLOT(startSearch())); + connect(m_searchUi->searchResults, SIGNAL(linkActivated(QString)), this, SLOT(onLinkActivated(QString))); connect(m_searchTimer, SIGNAL(timeout()), this, SLOT(search())); connect(closeAction, SIGNAL(triggered()), this, SLOT(closeSearch())); @@ -547,84 +547,117 @@ void DatabaseWidget::switchToImportKeepass1(const QString& fileName) setCurrentWidget(m_keepass1OpenWidget); } -void DatabaseWidget::toggleSearch() +void DatabaseWidget::search(const QString& searchString, bool caseSensitive, bool allGroups) { - if (m_entryView->inEntryListMode()) { - closeSearch(); + m_searchSensitivity = caseSensitive ? Qt::CaseSensitive + : Qt::CaseInsensitive; + m_searchAllGroups = allGroups; + search(searchString); +} + +void DatabaseWidget::search(const QString& text) +{ + if (text.isEmpty()) { + if (m_entryView->inEntryListMode()) + closeSearch(); + } + else if (m_entryView->inEntryListMode()) { + m_searchText = text; + startSearchTimer(); } else { - showSearch(); + showSearch(text); + } +} + +bool DatabaseWidget::caseSensitiveSearch() const +{ + return m_searchSensitivity == Qt::CaseSensitive; +} + +void DatabaseWidget::setCaseSensitiveSearch(bool caseSensitive) +{ + if (caseSensitive != caseSensitiveSearch()) { + m_searchSensitivity = caseSensitive ? Qt::CaseSensitive + : Qt::CaseInsensitive; + if (m_entryView->inEntryListMode()) + startSearchTimer(); + } +} + +bool DatabaseWidget::isAllGroupsSearch() const +{ + return m_searchAllGroups; +} + +bool DatabaseWidget::canChooseSearchScope() const +{ + return currentGroup() != m_db->rootGroup(); +} + +Group*DatabaseWidget::currentGroup() const +{ + return m_entryView->inEntryListMode() ? m_lastGroup + : m_groupView->currentGroup(); +} + +void DatabaseWidget::setAllGroupsSearch(bool allGroups) +{ + if (allGroups != isAllGroupsSearch()) { + m_searchAllGroups = allGroups; + if (m_entryView->inEntryListMode()) + startSearchTimer(); } } void DatabaseWidget::closeSearch() { Q_ASSERT(m_lastGroup); + m_searchTimer->stop(); m_groupView->setCurrentGroup(m_lastGroup); } void DatabaseWidget::showSearch(const QString & searchString) { - m_searchUi->searchEdit->blockSignals(true); - m_searchUi->searchEdit->setText(searchString); - m_searchUi->searchEdit->blockSignals(false); - - m_searchUi->searchCurrentRadioButton->blockSignals(true); - m_searchUi->searchRootRadioButton->blockSignals(true); - m_searchUi->searchRootRadioButton->setChecked(true); - m_searchUi->searchCurrentRadioButton->blockSignals(false); - m_searchUi->searchRootRadioButton->blockSignals(false); - + m_searchText = searchString; m_lastGroup = m_groupView->currentGroup(); Q_ASSERT(m_lastGroup); - - if (m_lastGroup == m_db->rootGroup()) { - m_searchUi->optionsWidget->hide(); - m_searchUi->searchCurrentRadioButton->hide(); - m_searchUi->searchRootRadioButton->hide(); - } - else { - m_searchUi->optionsWidget->show(); - m_searchUi->searchCurrentRadioButton->show(); - m_searchUi->searchRootRadioButton->show(); - m_searchUi->searchCurrentRadioButton->setText(tr("Current group") - .append(" (") - .append(m_lastGroup->name()) - .append(")")); - } m_groupView->setCurrentIndex(QModelIndex()); m_searchWidget->show(); search(); - m_searchUi->searchEdit->setFocus(); +} + +void DatabaseWidget::onLinkActivated(const QString& link) +{ + if (link == "searchAll") + setAllGroupsSearch(true); + else if (link == "searchCurrent") + setAllGroupsSearch(false); } void DatabaseWidget::search() { Q_ASSERT(m_lastGroup); - Group* searchGroup; - if (m_searchUi->searchCurrentRadioButton->isChecked()) { - searchGroup = m_lastGroup; - } - else if (m_searchUi->searchRootRadioButton->isChecked()) { - searchGroup = m_db->rootGroup(); - } - else { - Q_ASSERT(false); - return; - } + Group* searchGroup = m_searchAllGroups ? m_db->rootGroup() + : m_lastGroup; + QList searchResult = searchGroup->search(m_searchText, m_searchSensitivity); - Qt::CaseSensitivity sensitivity; - if (m_searchUi->caseSensitiveCheckBox->isChecked()) { - sensitivity = Qt::CaseSensitive; + QString message; + switch(searchResult.count()) { + case 0: message = tr("No result found"); break; + case 1: message = tr("1 result found"); break; + default: message = tr("%1 results found").arg(searchResult.count()); break; } - else { - sensitivity = Qt::CaseInsensitive; - } - QList searchResult = searchGroup->search(m_searchUi->searchEdit->text(), sensitivity); - + if (searchGroup != m_db->rootGroup()) + message += tr(" in \"%1\". Search all groups...").arg(searchGroup->name()); + else if (m_lastGroup != m_db->rootGroup()) + message += tr(". Search in \"%1\"...").arg(m_lastGroup->name()); + else + message += tr("."); + m_searchUi->searchResults->setText(message); m_entryView->setEntryList(searchResult); } @@ -639,6 +672,9 @@ void DatabaseWidget::startSearchTimer() void DatabaseWidget::startSearch() { + if (!isInSearchMode()) + return; + if (!m_searchTimer->isActive()) { m_searchTimer->stop(); } @@ -667,14 +703,14 @@ bool DatabaseWidget::canDeleteCurrentGoup() return !isRootGroup && !isRecycleBin; } -bool DatabaseWidget::isInSearchMode() +bool DatabaseWidget::isInSearchMode() const { return m_entryView->inEntryListMode(); } -QString DatabaseWidget::searchText() +QString DatabaseWidget::searchText() const { - return m_entryView->inEntryListMode() ? m_searchUi->searchEdit->text() : QString(); + return m_entryView->inEntryListMode() ? m_searchText : QString(); } void DatabaseWidget::clearLastGroup(Group* group) diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index 92cb79734..0be097724 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -63,14 +63,19 @@ public: Database* database(); bool dbHasKey(); bool canDeleteCurrentGoup(); - bool isInSearchMode(); - QString searchText(); + bool isInSearchMode() const; + QString searchText() const; + bool caseSensitiveSearch() const; + bool isAllGroupsSearch() const; + bool canChooseSearchScope() const; + Group* currentGroup() const; int addWidget(QWidget* w); void setCurrentIndex(int index); void setCurrentWidget(QWidget* widget); DatabaseWidget::Mode currentMode(); void lock(); void updateFilename(const QString& filename); + void search(const QString & searchString, bool caseSensitive, bool allGroups); Q_SIGNALS: void closeRequest(); @@ -102,12 +107,15 @@ public Q_SLOTS: void switchToOpenDatabase(const QString &fileName, const CompositeKey &masterKey); void switchToImportKeepass1(const QString& fileName); void switchToView(bool accepted); - void toggleSearch(); - void showSearch(const QString & searchString = QString()); + void search(const QString & searchString); + void setCaseSensitiveSearch(bool caseSensitive); + void setAllGroupsSearch(bool allGroups); void emitGroupContextMenuRequested(const QPoint& pos); void emitEntryContextMenuRequested(const QPoint& pos); private Q_SLOTS: + void onLinkActivated(const QString& link); + void showSearch(const QString & searchString = QString()); void switchBackToEntryEdit(); void switchToHistoryView(Entry* entry); void switchToEntryEdit(Entry* entry); @@ -145,6 +153,9 @@ private: QTimer* m_searchTimer; QWidget* widgetBeforeLock; QString m_filename; + QString m_searchText; + bool m_searchAllGroups; + Qt::CaseSensitivity m_searchSensitivity; }; #endif // KEEPASSX_DATABASEWIDGET_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 657ecce4b..9dab8e048 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -37,6 +37,7 @@ #include "http/HttpSettings.h" #include "http/OptionDialog.h" #include "gui/SettingsWidget.h" +#include "gui/qocoa/qsearchfield.h" class HttpPlugin: public ISettingsPage { public: @@ -114,7 +115,11 @@ MainWindow::MainWindow() setShortcut(m_ui->actionDatabaseClose, QKeySequence::Close, Qt::CTRL + Qt::Key_W); m_ui->actionLockDatabases->setShortcut(Qt::CTRL + Qt::Key_L); setShortcut(m_ui->actionQuit, QKeySequence::Quit, Qt::CTRL + Qt::Key_Q); - setShortcut(m_ui->actionSearch, QKeySequence::Find, Qt::CTRL + Qt::Key_F); + //TODO: do not register shortcut on Q_OS_MAC, if this is done automatically?? + const QKeySequence seq = !QKeySequence::keyBindings(QKeySequence::Find).isEmpty() + ? QKeySequence::Find + : QKeySequence(Qt::CTRL + Qt::Key_F); + connect(new QShortcut(seq, this), SIGNAL(activated()), m_ui->searchField, SLOT(setFocus())); m_ui->actionEntryNew->setShortcut(Qt::CTRL + Qt::Key_N); m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E); m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D); @@ -152,8 +157,6 @@ MainWindow::MainWindow() m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about")); - m_ui->actionSearch->setIcon(filePath()->icon("actions", "system-search")); - m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)), this, SLOT(setMenuActionState(DatabaseWidget::Mode))); m_actionMultiplexer.connect(SIGNAL(groupChanged()), @@ -225,8 +228,24 @@ MainWindow::MainWindow() connect(m_ui->actionAbout, SIGNAL(triggered()), SLOT(showAboutDialog())); - m_actionMultiplexer.connect(m_ui->actionSearch, SIGNAL(triggered()), - SLOT(toggleSearch())); + m_ui->searchField->setPlaceholderText(tr("Type to search")); + m_ui->searchField->setEnabled(false); + m_ui->toolBar->addWidget(m_ui->searchPanel); + m_actionMultiplexer.connect(m_ui->searchField, SIGNAL(textChanged(QString)), + SLOT(search(QString))); + QMenu* searchMenu = new QMenu(this); + searchMenu->addAction(m_ui->actionFindCaseSensitive); + searchMenu->addSeparator(); + searchMenu->addAction(m_ui->actionFindCurrentGroup); + searchMenu->addAction(m_ui->actionFindRootGroup); + m_ui->searchField->setMenu(searchMenu); + QActionGroup* group = new QActionGroup(this); + group->addAction(m_ui->actionFindCurrentGroup); + group->addAction(m_ui->actionFindRootGroup); + m_actionMultiplexer.connect(m_ui->actionFindCaseSensitive, SIGNAL(toggled(bool)), + SLOT(setCaseSensitiveSearch(bool))); + m_actionMultiplexer.connect(m_ui->actionFindRootGroup, SIGNAL(toggled(bool)), + SLOT(setAllGroupsSearch(bool))); m_ui->tabWidget->reopenLastDatabases(); } @@ -298,6 +317,26 @@ void MainWindow::openDatabase(const QString& fileName, const QString& pw, const m_ui->tabWidget->openDatabase(fileName, pw, keyFile); } +void MainWindow::updateSearchField(DatabaseWidget* dbWidget) +{ + bool enabled = dbWidget != NULL; + + m_ui->actionFindCaseSensitive->setChecked(enabled && dbWidget->caseSensitiveSearch()); + + m_ui->actionFindCurrentGroup->setEnabled(enabled && dbWidget->canChooseSearchScope()); + m_ui->actionFindRootGroup->setEnabled(enabled && dbWidget->canChooseSearchScope()); + if (enabled && dbWidget->isAllGroupsSearch()) + m_ui->actionFindRootGroup->setChecked(true); + else + m_ui->actionFindCurrentGroup->setChecked(true); + + m_ui->searchField->setEnabled(enabled); + if (enabled && dbWidget->isInSearchMode()) + m_ui->searchField->setText(dbWidget->searchText()); + else + m_ui->searchField->clear(); +} + void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) { bool inDatabaseTabWidget = (m_ui->stackedWidget->currentIndex() == 0); @@ -329,9 +368,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) m_ui->actionGroupNew->setEnabled(groupSelected); m_ui->actionGroupEdit->setEnabled(groupSelected); m_ui->actionGroupDelete->setEnabled(groupSelected && dbWidget->canDeleteCurrentGoup()); - m_ui->actionSearch->setEnabled(true); - // TODO: get checked state from db widget - m_ui->actionSearch->setChecked(inSearch); + updateSearchField(dbWidget); m_ui->actionChangeMasterKey->setEnabled(true); m_ui->actionChangeDatabaseSettings->setEnabled(true); m_ui->actionDatabaseSave->setEnabled(true); @@ -349,8 +386,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) } m_ui->menuEntryCopyAttribute->setEnabled(false); - m_ui->actionSearch->setEnabled(false); - m_ui->actionSearch->setChecked(false); + updateSearchField(); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); @@ -371,8 +407,7 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode) } m_ui->menuEntryCopyAttribute->setEnabled(false); - m_ui->actionSearch->setEnabled(false); - m_ui->actionSearch->setChecked(false); + updateSearchField(); m_ui->actionChangeMasterKey->setEnabled(false); m_ui->actionChangeDatabaseSettings->setEnabled(false); m_ui->actionDatabaseSave->setEnabled(false); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 52472a429..04e600992 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -64,6 +64,7 @@ private Q_SLOTS: void setToolbarIconSize28(); private: + void updateSearchField(DatabaseWidget* dbWidget = NULL); static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); static const QString BaseWindowTitle; diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui index ec05f82ee..8f04f118a 100644 --- a/src/gui/MainWindow.ui +++ b/src/gui/MainWindow.ui @@ -55,6 +55,34 @@ + + + + + 0 + 0 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + @@ -173,7 +201,6 @@ - @@ -299,17 +326,6 @@ Clone entry - - - true - - - false - - - Find - - false @@ -379,6 +395,30 @@ 2&8x28 + + + true + + + Case Sensitive + + + + + true + + + Current Group + + + + + true + + + Root Group + + @@ -399,6 +439,12 @@
gui/WelcomeWidget.h
1
+ + QSearchField + QWidget +
gui/qocoa/qsearchfield.h
+ 1 +
diff --git a/src/gui/SearchWidget.ui b/src/gui/SearchWidget.ui index d6205768a..b8be91f32 100644 --- a/src/gui/SearchWidget.ui +++ b/src/gui/SearchWidget.ui @@ -7,17 +7,14 @@ 0 0 630 - 87 + 34 0 - - - - + @@ -27,72 +24,12 @@ - - - Find: - - + - - - - - 0 - - - - - Case sensitive - - - - - - - Current group - - - false - - - - - - - Root group - - - true - - - - - - - Qt::Horizontal - - - - 255 - 1 - - - - - - - - - - LineEdit - QLineEdit -
gui/LineEdit.h
-
-
diff --git a/src/gui/qocoa/CMakeLists.txt b/src/gui/qocoa/CMakeLists.txt new file mode 100644 index 000000000..7ea00cc65 --- /dev/null +++ b/src/gui/qocoa/CMakeLists.txt @@ -0,0 +1,50 @@ +project(Qocoa) +cmake_minimum_required(VERSION 2.8) + +#find_package(Qt4 COMPONENTS QtMain QtCore QtGui REQUIRED) +#include(UseQt4) + +set(SOURCES + #main.cpp + #gallery.cpp +) + +set(HEADERS + #gallery.h + qsearchfield.h + qbutton.h + qprogressindicatorspinning.h +) + +qt4_wrap_cpp(MOC_SOURCES ${HEADERS}) + +if(APPLE) + list(APPEND SOURCES + qsearchfield_mac.mm + qbutton_mac.mm + qprogressindicatorspinning_mac.mm + ) +else() + list(APPEND SOURCES + qsearchfield_nonmac.cpp + qbutton_nonmac.cpp + qprogressindicatorspinning_nonmac.cpp + ) + set(RESOURCES + qsearchfield_nonmac.qrc + qprogressindicatorspinning_nonmac.qrc + ) + qt4_add_resources(RESOURCES_SOURCES ${RESOURCES}) +endif() + +#add_executable(Qocoa +# WIN32 MACOSX_BUNDLE +# ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES} +#) +#target_link_libraries(Qocoa ${QT_LIBRARIES}) + +add_library (Qocoa STATIC ${SOURCES} ${MOC_SOURCES} ${HEADERS} ${RESOURCES_SOURCES}) + +if(APPLE) + set_target_properties(Qocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit") +endif() diff --git a/src/gui/qocoa/LICENSE.txt b/src/gui/qocoa/LICENSE.txt new file mode 100644 index 000000000..910eb6d20 --- /dev/null +++ b/src/gui/qocoa/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/gui/qocoa/Qocoa.pro b/src/gui/qocoa/Qocoa.pro new file mode 100644 index 000000000..8b325d192 --- /dev/null +++ b/src/gui/qocoa/Qocoa.pro @@ -0,0 +1,17 @@ +SOURCES += main.cpp\ + gallery.cpp \ + +HEADERS += gallery.h \ + qocoa_mac.h \ + qsearchfield.h \ + qbutton.h \ + qprogressindicatorspinning.h \ + +mac { + OBJECTIVE_SOURCES += qsearchfield_mac.mm qbutton_mac.mm qprogressindicatorspinning_mac.mm + LIBS += -framework Foundation -framework Appkit + QMAKE_CFLAGS += -mmacosx-version-min=10.6 +} else { + SOURCES += qsearchfield_nonmac.cpp qbutton_nonmac.cpp qprogressindicatorspinning_nonmac.cpp + RESOURCES += qsearchfield_nonmac.qrc qprogressindicatorspinning_nonmac.qrc +} diff --git a/src/gui/qocoa/README.md b/src/gui/qocoa/README.md new file mode 100644 index 000000000..5f981893e --- /dev/null +++ b/src/gui/qocoa/README.md @@ -0,0 +1,36 @@ +# Qocoa +Qocoa is a collection of Qt wrappers for OSX's Cocoa widgets. + +## Features +- basic fallback to sensible Qt types on non-OSX platforms +- shared class headers which expose no implementation details +- typical Qt signal/slot-based API +- trivial to import into projects (class header/implementation, [single shared global header](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h)) + +## Building +``` +git clone git://github.com/mikemcquaid/Qocoa.git +cd Qocoa +qmake # or cmake . +make +``` + +## Status +Qocoa classes are currently provided for NSButton, a spinning NSProgressIndicator and NSSearchField. There is a [TODO list](https://github.com/mikemcquaid/Qocoa/blob/master/TODO.md) for classes I hope to implement. + +## Usage +For each class you want to use copy the [`qocoa_mac.h`](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h), `$CLASS.h`, `$CLASS_mac.*` and `$CLASS_nonmac.*` files into your source tree and add them to your buildsystem. Examples are provided for [CMake](https://github.com/mikemcquaid/Qocoa/blob/master/CMakeLists.txt) and [QMake](https://github.com/mikemcquaid/Qocoa/blob/master/Qocoa.pro). + +## Contact +[Mike McQuaid](mailto:mike@mikemcquaid.com) + +## License +Qocoa is licensed under the [MIT License](http://en.wikipedia.org/wiki/MIT_License). +The full license text is available in [LICENSE.txt](https://github.com/mikemcquaid/Qocoa/blob/master/LICENSE.txt). + +Magnifier and EditClear icons taken from [QtCreator](http://qt-project.org/), licensed under [LGPL](http://www.gnu.org/copyleft/lesser.html). + +Other icons are taken from the [Oxygen Project](http://www.oxygen-icons.org/) and are licensed under the [Creative Commons Attribution-ShareAlike 3.0 License](http://creativecommons.org/licenses/by-sa/3.0/). + +## Gallery +![Qocoa Gallery](https://github.com/mikemcquaid/Qocoa/raw/master/gallery.png) diff --git a/src/gui/qocoa/TODO.md b/src/gui/qocoa/TODO.md new file mode 100644 index 000000000..45972bafa --- /dev/null +++ b/src/gui/qocoa/TODO.md @@ -0,0 +1,13 @@ +Widgets I hope to implement (or at least investigate): + +- NSTokenField +- NSSegmentedControl +- NSLevelIndicator +- NSPathControl +- NSSlider (Circular) +- NSSplitView +- NSTextFinder +- NSOutlineView in an NSScrollView (Source List) +- NSDrawer +- PDFView +- WebView diff --git a/src/gui/qocoa/gallery.cpp b/src/gui/qocoa/gallery.cpp new file mode 100644 index 000000000..210821ebd --- /dev/null +++ b/src/gui/qocoa/gallery.cpp @@ -0,0 +1,75 @@ +#include "gallery.h" + +#include + +#include "qsearchfield.h" +#include "qbutton.h" +#include "qprogressindicatorspinning.h" + +Gallery::Gallery(QWidget *parent) : QWidget(parent) +{ + setWindowTitle("Qocoa Gallery"); + QVBoxLayout *layout = new QVBoxLayout(this); + + QSearchField *searchField = new QSearchField(this); + layout->addWidget(searchField); + + QSearchField *searchFieldPlaceholder = new QSearchField(this); + searchFieldPlaceholder->setPlaceholderText("Placeholder text"); + layout->addWidget(searchFieldPlaceholder); + + QButton *roundedButton = new QButton(this, QButton::Rounded); + roundedButton->setText("Button"); + layout->addWidget(roundedButton); + + QButton *regularSquareButton = new QButton(this, QButton::RegularSquare); + regularSquareButton->setText("Button"); + layout->addWidget(regularSquareButton); + + QButton *disclosureButton = new QButton(this, QButton::Disclosure); + layout->addWidget(disclosureButton); + + QButton *shadowlessSquareButton = new QButton(this, QButton::ShadowlessSquare); + shadowlessSquareButton->setText("Button"); + layout->addWidget(shadowlessSquareButton); + + QButton *circularButton = new QButton(this, QButton::Circular); + layout->addWidget(circularButton); + + QButton *textureSquareButton = new QButton(this, QButton::TexturedSquare); + textureSquareButton->setText("Textured Button"); + layout->addWidget(textureSquareButton); + + QButton *helpButton = new QButton(this, QButton::HelpButton); + layout->addWidget(helpButton); + + QButton *smallSquareButton = new QButton(this, QButton::SmallSquare); + smallSquareButton->setText("Gradient Button"); + layout->addWidget(smallSquareButton); + + QButton *texturedRoundedButton = new QButton(this, QButton::TexturedRounded); + texturedRoundedButton->setText("Round Textured"); + layout->addWidget(texturedRoundedButton); + + QButton *roundedRectangleButton = new QButton(this, QButton::RoundRect); + roundedRectangleButton->setText("Rounded Rect Button"); + layout->addWidget(roundedRectangleButton); + + QButton *recessedButton = new QButton(this, QButton::Recessed); + recessedButton->setText("Recessed Button"); + layout->addWidget(recessedButton); + + QButton *roundedDisclosureButton = new QButton(this, QButton::RoundedDisclosure); + layout->addWidget(roundedDisclosureButton); + +#ifdef __MAC_10_7 + QButton *inlineButton = new QButton(this, QButton::Inline); + inlineButton->setText("Inline Button"); + layout->addWidget(inlineButton); +#endif + + + QProgressIndicatorSpinning *progressIndicatorSpinning = new QProgressIndicatorSpinning(this); + progressIndicatorSpinning->animate(); + layout->addWidget(progressIndicatorSpinning); +} diff --git a/src/gui/qocoa/gallery.h b/src/gui/qocoa/gallery.h new file mode 100644 index 000000000..1e83bad94 --- /dev/null +++ b/src/gui/qocoa/gallery.h @@ -0,0 +1,14 @@ +#ifndef GALLERY_H +#define GALLERY_H + +#include + +class Gallery : public QWidget +{ + Q_OBJECT + +public: + explicit Gallery(QWidget *parent = 0); +}; + +#endif // WIDGET_H diff --git a/src/gui/qocoa/gallery.png b/src/gui/qocoa/gallery.png new file mode 100644 index 0000000000000000000000000000000000000000..7a2736ff572491af1fefbbface2742caceabd6e3 GIT binary patch literal 42195 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 z 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 N002ovPDHLkV1hfpIk5l$ literal 0 HcmV?d00001 diff --git a/src/gui/qocoa/main.cpp b/src/gui/qocoa/main.cpp new file mode 100644 index 000000000..33e7eb8d7 --- /dev/null +++ b/src/gui/qocoa/main.cpp @@ -0,0 +1,12 @@ +#include +#include "gallery.h" + +int main(int argc, char *argv[]) +{ + QApplication application(argc, argv); + + Gallery gallery; + gallery.show(); + + return application.exec(); +} diff --git a/src/gui/qocoa/qbutton.h b/src/gui/qocoa/qbutton.h new file mode 100644 index 000000000..8b8b7a74f --- /dev/null +++ b/src/gui/qocoa/qbutton.h @@ -0,0 +1,49 @@ +#ifndef QBUTTON_H +#define QBUTTON_H + +#include +#include + +class QButtonPrivate; +class QButton : public QWidget +{ + Q_OBJECT +public: + // Matches NSBezelStyle + enum BezelStyle { + Rounded = 1, + RegularSquare = 2, + Disclosure = 5, + ShadowlessSquare = 6, + Circular = 7, + TexturedSquare = 8, + HelpButton = 9, + SmallSquare = 10, + TexturedRounded = 11, + RoundRect = 12, + Recessed = 13, + RoundedDisclosure = 14, +#ifdef __MAC_10_7 + Inline = 15 +#endif + }; + + explicit QButton(QWidget *parent, BezelStyle bezelStyle = Rounded); + +public Q_SLOTS: + void setText(const QString &text); + void setImage(const QPixmap &image); + void setChecked(bool checked); + +public: + void setCheckable(bool checkable); + bool isChecked(); + +Q_SIGNALS: + void clicked(bool checked = false); + +private: + friend class QButtonPrivate; + QPointer pimpl; +}; +#endif // QBUTTON_H diff --git a/src/gui/qocoa/qbutton_mac.mm b/src/gui/qocoa/qbutton_mac.mm new file mode 100644 index 000000000..15490e453 --- /dev/null +++ b/src/gui/qocoa/qbutton_mac.mm @@ -0,0 +1,229 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qbutton.h" + +#include "qocoa_mac.h" + +#import "Foundation/NSAutoreleasePool.h" +#import "AppKit/NSButton.h" +#import "AppKit/NSFont.h" + +class QButtonPrivate : public QObject +{ +public: + QButtonPrivate(QButton *qButton, NSButton *nsButton, QButton::BezelStyle bezelStyle) + : QObject(qButton), qButton(qButton), nsButton(nsButton) + { + switch(bezelStyle) { + case QButton::Disclosure: + case QButton::Circular: +#ifdef __MAC_10_7 + case QButton::Inline: +#endif + case QButton::RoundedDisclosure: + case QButton::HelpButton: + [nsButton setTitle:@""]; + default: + break; + } + + NSFont* font = 0; + switch(bezelStyle) { + case QButton::RoundRect: + font = [NSFont fontWithName:@"Lucida Grande" size:12]; + break; + + case QButton::Recessed: + font = [NSFont fontWithName:@"Lucida Grande Bold" size:12]; + break; + +#ifdef __MAC_10_7 + case QButton::Inline: + font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; + break; +#endif + + default: + font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; + break; + } + [nsButton setFont:font]; + + switch(bezelStyle) { + case QButton::Rounded: + qButton->setMinimumWidth(40); + qButton->setFixedHeight(24); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::RegularSquare: + case QButton::TexturedSquare: + qButton->setMinimumSize(14, 23); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::ShadowlessSquare: + qButton->setMinimumSize(5, 25); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::SmallSquare: + qButton->setMinimumSize(4, 21); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::TexturedRounded: + qButton->setMinimumSize(10, 22); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::RoundRect: + case QButton::Recessed: + qButton->setMinimumWidth(16); + qButton->setFixedHeight(18); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::Disclosure: + qButton->setMinimumWidth(13); + qButton->setFixedHeight(13); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; + case QButton::Circular: + qButton->setMinimumSize(16, 16); + qButton->setMaximumHeight(40); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + break; + case QButton::HelpButton: + case QButton::RoundedDisclosure: + qButton->setMinimumWidth(22); + qButton->setFixedHeight(22); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; +#ifdef __MAC_10_7 + case QButton::Inline: + qButton->setMinimumWidth(10); + qButton->setFixedHeight(16); + qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + break; +#endif + } + + switch(bezelStyle) { + case QButton::Recessed: + [nsButton setButtonType:NSPushOnPushOffButton]; + case QButton::Disclosure: + [nsButton setButtonType:NSOnOffButton]; + default: + [nsButton setButtonType:NSMomentaryPushInButton]; + } + + [nsButton setBezelStyle:bezelStyle]; + } + + void clicked() + { + emit qButton->clicked(qButton->isChecked()); + } + + ~QButtonPrivate() { + [[nsButton target] release]; + [nsButton setTarget:nil]; + } + + QButton *qButton; + NSButton *nsButton; +}; + +@interface QButtonTarget : NSObject +{ +@public + QPointer pimpl; +} +-(void)clicked; +@end + +@implementation QButtonTarget +-(void)clicked { + Q_ASSERT(pimpl); + if (pimpl) + pimpl->clicked(); +} +@end + +QButton::QButton(QWidget *parent, BezelStyle bezelStyle) : QWidget(parent) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSButton *button = [[NSButton alloc] init]; + pimpl = new QButtonPrivate(this, button, bezelStyle); + + QButtonTarget *target = [[QButtonTarget alloc] init]; + target->pimpl = pimpl; + [button setTarget:target]; + + [button setAction:@selector(clicked)]; + + setupLayout(button, this); + + [button release]; + + [pool drain]; +} + +void QButton::setText(const QString &text) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pimpl->nsButton setTitle:fromQString(text)]; + [pool drain]; +} + +void QButton::setImage(const QPixmap &image) +{ + Q_ASSERT(pimpl); + if (pimpl) + [pimpl->nsButton setImage:fromQPixmap(image)]; +} + +void QButton::setChecked(bool checked) +{ + Q_ASSERT(pimpl); + if (pimpl) + [pimpl->nsButton setState:checked]; +} + +void QButton::setCheckable(bool checkable) +{ + const NSInteger cellMask = checkable ? NSChangeBackgroundCellMask : NSNoCellMask; + + Q_ASSERT(pimpl); + if (pimpl) + [[pimpl->nsButton cell] setShowsStateBy:cellMask]; +} + +bool QButton::isChecked() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return false; + + return [pimpl->nsButton state]; +} diff --git a/src/gui/qocoa/qbutton_nonmac.cpp b/src/gui/qocoa/qbutton_nonmac.cpp new file mode 100644 index 000000000..c7fafe6e4 --- /dev/null +++ b/src/gui/qocoa/qbutton_nonmac.cpp @@ -0,0 +1,89 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qbutton.h" + +#include +#include +#include +#include + +class QButtonPrivate : public QObject +{ +public: + QButtonPrivate(QButton *button, QAbstractButton *abstractButton) + : QObject(button), abstractButton(abstractButton) {} + QPointer abstractButton; +}; + +QButton::QButton(QWidget *parent, BezelStyle) : QWidget(parent) +{ + QAbstractButton *button = 0; + if (qobject_cast(parent)) + button = new QToolButton(this); + else + button = new QPushButton(this); + connect(button, SIGNAL(clicked()), + this, SIGNAL(clicked())); + pimpl = new QButtonPrivate(this, button); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(button); +} + +void QButton::setText(const QString &text) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setText(text); +} + +void QButton::setImage(const QPixmap &image) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setIcon(image); +} + +void QButton::setChecked(bool checked) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setChecked(checked); +} + +void QButton::setCheckable(bool checkable) +{ + Q_ASSERT(pimpl); + if (pimpl) + pimpl->abstractButton->setCheckable(checkable); +} + +bool QButton::isChecked() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return false; + + return pimpl->abstractButton->isChecked(); +} diff --git a/src/gui/qocoa/qocoa_mac.h b/src/gui/qocoa/qocoa_mac.h new file mode 100644 index 000000000..ced431173 --- /dev/null +++ b/src/gui/qocoa/qocoa_mac.h @@ -0,0 +1,54 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include +#include +#include +#include + +static inline NSString* fromQString(const QString &string) +{ + const QByteArray utf8 = string.toUtf8(); + const char* cString = utf8.constData(); + return [[NSString alloc] initWithUTF8String:cString]; +} + +static inline QString toQString(NSString *string) +{ + if (!string) + return QString(); + return QString::fromUtf8([string UTF8String]); +} + +static inline NSImage* fromQPixmap(const QPixmap &pixmap) +{ + CGImageRef cgImage = pixmap.toMacCGImageRef(); + return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; +} + +static inline void setupLayout(void *cocoaView, QWidget *parent) +{ + parent->setAttribute(Qt::WA_NativeWindow); + QVBoxLayout *layout = new QVBoxLayout(parent); + layout->setMargin(0); + layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); +} diff --git a/src/gui/qocoa/qprogressindicatorspinning.h b/src/gui/qocoa/qprogressindicatorspinning.h new file mode 100644 index 000000000..d78a4868d --- /dev/null +++ b/src/gui/qocoa/qprogressindicatorspinning.h @@ -0,0 +1,29 @@ +#ifndef QPROGRESSINDICATORSPINNING_H +#define QPROGRESSINDICATORSPINNING_H + +#include +#include + +class QProgressIndicatorSpinningPrivate; +class QProgressIndicatorSpinning : public QWidget +{ + Q_OBJECT +public: + // Matches NSProgressIndicatorThickness + enum Thickness { + Default = 14, + Small = 10, + Large = 18, + Aqua = 12 + }; + + explicit QProgressIndicatorSpinning(QWidget *parent, + Thickness thickness = Default); +public Q_SLOTS: + void animate(bool animate = true); +private: + friend class QProgressIndicatorSpinningPrivate; + QPointer pimpl; +}; + +#endif // QPROGRESSINDICATORSPINNING_H diff --git a/src/gui/qocoa/qprogressindicatorspinning_mac.mm b/src/gui/qocoa/qprogressindicatorspinning_mac.mm new file mode 100644 index 000000000..c67c7c567 --- /dev/null +++ b/src/gui/qocoa/qprogressindicatorspinning_mac.mm @@ -0,0 +1,70 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qprogressindicatorspinning.h" + +#include "qocoa_mac.h" + +#import "Foundation/NSAutoreleasePool.h" +#import "AppKit/NSProgressIndicator.h" + +class QProgressIndicatorSpinningPrivate : public QObject +{ +public: + QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning, + NSProgressIndicator *nsProgressIndicator) + : QObject(qProgressIndicatorSpinning), nsProgressIndicator(nsProgressIndicator) {} + + NSProgressIndicator *nsProgressIndicator; +}; + +QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent, + Thickness thickness) + : QWidget(parent) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSProgressIndicator *progress = [[NSProgressIndicator alloc] init]; + [progress setStyle:NSProgressIndicatorSpinningStyle]; + + pimpl = new QProgressIndicatorSpinningPrivate(this, progress); + + setupLayout(progress, this); + + setFixedSize(thickness, thickness); + + [progress release]; + + [pool drain]; +} + +void QProgressIndicatorSpinning::animate(bool animate) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + if (animate) + [pimpl->nsProgressIndicator startAnimation:nil]; + else + [pimpl->nsProgressIndicator stopAnimation:nil]; +} diff --git a/src/gui/qocoa/qprogressindicatorspinning_nonmac.cpp b/src/gui/qocoa/qprogressindicatorspinning_nonmac.cpp new file mode 100644 index 000000000..fae777830 --- /dev/null +++ b/src/gui/qocoa/qprogressindicatorspinning_nonmac.cpp @@ -0,0 +1,86 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qprogressindicatorspinning.h" + +#include +#include +#include + +class QProgressIndicatorSpinningPrivate : public QObject +{ +public: + QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning, + QMovie *movie) + : QObject(qProgressIndicatorSpinning), movie(movie) {} + + QPointer movie; +}; + +struct QProgressIndicatorSpinningResources +{ +#ifndef Q_OS_MAC + QProgressIndicatorSpinningResources() { + Q_INIT_RESOURCE(qprogressindicatorspinning_nonmac); + } + ~QProgressIndicatorSpinningResources() { + Q_CLEANUP_RESOURCE(qprogressindicatorspinning_nonmac); + } +#endif +}; + +QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent, + Thickness thickness) + : QWidget(parent) +{ + static QProgressIndicatorSpinningResources resources; + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + QSize size(thickness, thickness); + QMovie *movie = new QMovie(this); + movie->setFileName(":/Qocoa/qprogressindicatorspinning_nonmac.gif"); + movie->setScaledSize(size); + // Roughly match OSX speed. + movie->setSpeed(200); + pimpl = new QProgressIndicatorSpinningPrivate(this, movie); + + QLabel *label = new QLabel(this); + label->setMovie(movie); + + layout->addWidget(label); + setFixedSize(size); +} + + +void QProgressIndicatorSpinning::animate(bool animate) +{ + Q_ASSERT(pimpl && pimpl->movie); + if (!(pimpl && pimpl->movie)) + return; + + if (animate) + pimpl->movie->start(); + else + pimpl->movie->stop(); +} diff --git a/src/gui/qocoa/qprogressindicatorspinning_nonmac.gif b/src/gui/qocoa/qprogressindicatorspinning_nonmac.gif new file mode 100644 index 0000000000000000000000000000000000000000..3288d1035d70bb86517e2c233f1a904e41f06b29 GIT binary patch literal 3208 zcmc(iX;4#H9>pJdFE7h`I{IF)0|5<6L}(j=N}5%L009EB2nYfyF)E0PvIqo$u!IC; z4PgyY5|S9AEh38G)(9eq4TbH7_UHg@yWrlIJ$6smIADL7s^P;_O;ykRc9soXl`UC*LwQJXkii*0rx|*7rI2=x7WaRkx_~XZqFJ8R3c=2Kg zf@aSAv8+BJ8+^hyay>(QR@t*blbKzsf0}bscEqRc5Hd3o(-N5RyW=zWB*zQw6Zh>* z2CROCDAbu#D`)S|J_o(lL9Yn3l*+8RdiRD_>iNz$#_IAzCna&Wl5 zSF_(rRCDD!wi#i8oAm&jYtn2_@VB%2-H*G%bN#|(6R6N?wM)3u`PiGzwuX7qmTgyF zpE)h0kuoxQ9?=kW7Y!=R@DmhU9)vwT*EZWzJ zrt+=2tqFts72yIp?|gvdLhs8Hfku^Z(){gmN%Y=K#P|%fkvgUj~HfIp3CuXqCtYGtJ#me+n+-LmP( z*XNuk%!aH8bIE@_Bj46>M*dSro|7<6vZ7WUHh5YQzN$>IJFqCb|CT!wj~R2C2%=q{ zpt8rzY$aw?W?=Ustv{jo?Ow@ZRkLe<)NItY>Cyhle*wR59dTdF6(@{5^ zAQBOB*hNtc3bkY-8{Cm$nFS@elbTtSqrt7MB{h_4y+~`!mVa}?c&N>&?P}GqdMuhQ z&@TD5Czd((DcG_Su~dKKV)Pj$-qi1WHM8_vc^O4?^!oY|tmK~i!{fjd&@_1E(T~r7 z_REZy&hMT^ySJB3W7l$4YhR`M(J7S5S~+4Q&3HPa)z%zPpisOp$^ zTEe99ig2$5_qFr!$;7A6CJ}PJmRhli>w?LC}Y`#HLGy6 zMU4EhL~dKCN5Ut;U2jd*83ShBNiu zcJB0l9>1Modc?-oM<R4?}3g}UJ%@K);kriq>)e*rh%hdqM)5Q)*+O8 zXm;SEbs@koiYS!9YXIclSg+5m_s~yrW#kKMdiRszg(gCP5HPmP7L)vCf8@fxUh6qY z@Z#TmkjzAZX{rwE+q|K~F2v5{_@vt%>yT_a#fF03SFt{0RXvDAiaY~K9CgS1O>frXgAjBCS}mEd4mIWZ$=ovd5| zR?GRdU}d6+Q`+JRW)|=v7$)XNkn3yE`!nAiSCvOB1jKT zG<1aK3s<0b0m==egTD#8i(Of=1pGDTOCho0XpIOMQ&P87cVKY1W=C6kIg z9cH=@a&zbm2+`|{(_?YC9fdm?1TY~-pwlBn?>=(~1pDKbco6jloP;0-cqRiwV1A_S zEyV0Dj8Pwy!nekzaN>{)7rgZ&_QLxK{~1yRe865^yx>}+a!ECd>#MMwddow z@CU{l+Rt$xuXuf}?ga{3IAr?Raql^c@a%sI0U5m}HvJ5O1#I%_MMPt#BH>OqUZ{-k zt>4Xzz=%jT*FVW(uYkWyx}9Gw$HdN*qU?Bit#ji(Wi7p-u|_8?h^%szIS^s^fNM}b zgGy>|=cbEufpguY5_6w~&ZLv=Bo06UF9EYIY;Er-1VK)SyF&!|J{axiE1z^(hXwVq zsFS=K-#zC}CcOs^8W{KAt+kK)jYDgDYbCXv{{rwsgqtIU3<910$CJi)s?? z_t8k{>7*0~4l~LLF7$WXT5OSq5QCTbP_l!SN|{R}3D&eWA8~0ltWh1IL+ZBX4rRSt zWF6Om3WDMu4xK^1(BF`2cL}rUCzhHAB`@j5&R-yk_l*t;mPGY|u2^o|myvcOdrg0W z%=lX;f^Vkqfp?u7*4qQq%A3Mpf!xspWBSKS@O%r*TSM}?dl(@*%{0Jm_8;(h{R__M Bt + + qprogressindicatorspinning_nonmac.gif + + diff --git a/src/gui/qocoa/qsearchfield.h b/src/gui/qocoa/qsearchfield.h new file mode 100644 index 000000000..1583b4627 --- /dev/null +++ b/src/gui/qocoa/qsearchfield.h @@ -0,0 +1,48 @@ +#ifndef QSEARCHFIELD_H +#define QSEARCHFIELD_H + +#include +#include +#include + +class QSearchFieldPrivate; +class QSearchField : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); + Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText); + +public: + explicit QSearchField(QWidget *parent); + + QString text() const; + QString placeholderText() const; + void setFocus(Qt::FocusReason); + void setMenu(QMenu *menu); + +public Q_SLOTS: + void setText(const QString &text); + void setPlaceholderText(const QString &text); + void clear(); + void selectAll(); + void setFocus(); + +Q_SIGNALS: + void textChanged(const QString &text); + void editingFinished(); + void returnPressed(); + +private Q_SLOTS: + void popupMenu(); + +protected: + void changeEvent(QEvent*); + void resizeEvent(QResizeEvent*); + +private: + friend class QSearchFieldPrivate; + QPointer pimpl; +}; + +#endif // QSEARCHFIELD_H diff --git a/src/gui/qocoa/qsearchfield_mac.mm b/src/gui/qocoa/qsearchfield_mac.mm new file mode 100644 index 000000000..7b43ecc75 --- /dev/null +++ b/src/gui/qocoa/qsearchfield_mac.mm @@ -0,0 +1,257 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qsearchfield.h" + +#include "qocoa_mac.h" + +#import "Foundation/NSAutoreleasePool.h" +#import "Foundation/NSNotification.h" +#import "AppKit/NSSearchField.h" + +#include +#include + +#define KEYCODE_A 0 +#define KEYCODE_X 7 +#define KEYCODE_C 8 +#define KEYCODE_V 9 + +class QSearchFieldPrivate : public QObject +{ +public: + QSearchFieldPrivate(QSearchField *qSearchField, NSSearchField *nsSearchField) + : QObject(qSearchField), qSearchField(qSearchField), nsSearchField(nsSearchField) {} + + void textDidChange(const QString &text) + { + if (qSearchField) + emit qSearchField->textChanged(text); + } + + void textDidEndEditing() + { + if (qSearchField) + emit qSearchField->editingFinished(); + } + + void returnPressed() + { + if (qSearchField) + emit qSearchField->returnPressed(); + } + + QPointer qSearchField; + NSSearchField *nsSearchField; +}; + +@interface QSearchFieldDelegate : NSObject +{ +@public + QPointer pimpl; +} +-(void)controlTextDidChange:(NSNotification*)notification; +-(void)controlTextDidEndEditing:(NSNotification*)notification; +@end + +@implementation QSearchFieldDelegate +-(void)controlTextDidChange:(NSNotification*)notification { + Q_ASSERT(pimpl); + if (pimpl) + pimpl->textDidChange(toQString([[notification object] stringValue])); +} + +-(void)controlTextDidEndEditing:(NSNotification*)notification { + Q_UNUSED(notification); + // No Q_ASSERT here as it is called on destruction. + if (pimpl) + pimpl->textDidEndEditing(); + + if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement) + pimpl->returnPressed(); +} +@end + +@interface QocoaSearchField : NSSearchField +-(BOOL)performKeyEquivalent:(NSEvent*)event; +@end + +@implementation QocoaSearchField +-(BOOL)performKeyEquivalent:(NSEvent*)event { + if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask) + { + const unsigned short keyCode = [event keyCode]; + if (keyCode == KEYCODE_A) + { + [self performSelector:@selector(selectText:)]; + return YES; + } + else if (keyCode == KEYCODE_C) + { + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(toQString([self stringValue])); + return YES; + } + else if (keyCode == KEYCODE_V) + { + QClipboard* clipboard = QApplication::clipboard(); + [self setStringValue:fromQString(clipboard->text())]; + return YES; + } + else if (keyCode == KEYCODE_X) + { + QClipboard* clipboard = QApplication::clipboard(); + clipboard->setText(toQString([self stringValue])); + [self setStringValue:@""]; + return YES; + } + } + + return NO; +} +@end + +QSearchField::QSearchField(QWidget *parent) : QWidget(parent) +{ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSSearchField *search = [[QocoaSearchField alloc] init]; + + QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init]; + pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search); + [search setDelegate:delegate]; + + setupLayout(search, this); + + setFixedHeight(24); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + [search release]; + + [pool drain]; +} + +void QSearchField::setMenu(QMenu *menu) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + NSMenu *nsMenu = menu->macMenu(); + [[pimpl->nsSearchField cell] setSearchMenuTemplate:nsMenu]; +} + +void QSearchField::popupMenu() +{ +} + +void QSearchField::setText(const QString &text) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [pimpl->nsSearchField setStringValue:fromQString(text)]; + [pool drain]; +} + +void QSearchField::setPlaceholderText(const QString &text) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + [[pimpl->nsSearchField cell] setPlaceholderString:fromQString(text)]; + [pool drain]; +} + +void QSearchField::clear() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->nsSearchField setStringValue:@""]; + emit textChanged(QString()); +} + +void QSearchField::selectAll() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + [pimpl->nsSearchField performSelector:@selector(selectText:)]; +} + +QString QSearchField::text() const +{ + Q_ASSERT(pimpl); + if (!pimpl) + return QString(); + + return toQString([pimpl->nsSearchField stringValue]); +} + +QString QSearchField::placeholderText() const +{ + Q_ASSERT(pimpl); + if (!pimpl) + return QString(); + + return toQString([[pimpl->nsSearchField cell] placeholderString]); +} + +void QSearchField::setFocus(Qt::FocusReason) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + if ([pimpl->nsSearchField acceptsFirstResponder]) + [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField]; +} + +void QSearchField::setFocus() +{ + setFocus(Qt::OtherFocusReason); +} + +void QSearchField::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::EnabledChange) { + Q_ASSERT(pimpl); + if (!pimpl) + return; + + const bool enabled = isEnabled(); + [pimpl->nsSearchField setEnabled: enabled] + } + QWidget::changeEvent(event); +} + +void QSearchField::resizeEvent(QResizeEvent *resizeEvent) +{ + QWidget::resizeEvent(resizeEvent); +} diff --git a/src/gui/qocoa/qsearchfield_nonmac.cpp b/src/gui/qocoa/qsearchfield_nonmac.cpp new file mode 100644 index 000000000..5244bd605 --- /dev/null +++ b/src/gui/qocoa/qsearchfield_nonmac.cpp @@ -0,0 +1,270 @@ +/* +Copyright (C) 2011 by Mike McQuaid + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +#include "qsearchfield.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +class QSearchFieldPrivate : public QObject +{ +public: + QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton, QToolButton *searchButton) + : QObject(searchField), lineEdit(lineEdit), clearButton(clearButton), searchButton(searchButton) {} + + int lineEditFrameWidth() const { + return lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + } + + int clearButtonPaddedWidth() const { + return clearButton->width() + lineEditFrameWidth() * 2; + } + + int clearButtonPaddedHeight() const { + return clearButton->height() + lineEditFrameWidth() * 2; + } + + int searchButtonPaddedWidth() const { + return searchButton->width() + lineEditFrameWidth() * 2; + } + + int searchButtonPaddedHeight() const { + return searchButton->height() + lineEditFrameWidth() * 2; + } + + QPointer lineEdit; + QPointer clearButton; + QPointer searchButton; + QPointer searchMenu; +}; + +struct QSearchFieldResources +{ +#ifndef Q_OS_MAC + QSearchFieldResources() { + Q_INIT_RESOURCE(qsearchfield_nonmac); + } + ~QSearchFieldResources() { + Q_CLEANUP_RESOURCE(qsearchfield_nonmac); + } +#endif +}; + +QSearchField::QSearchField(QWidget *parent) : QWidget(parent) +{ + static QSearchFieldResources resources; + + QLineEdit *lineEdit = new QLineEdit(this); + connect(lineEdit, SIGNAL(textChanged(QString)), + this, SIGNAL(textChanged(QString))); + connect(lineEdit, SIGNAL(editingFinished()), + this, SIGNAL(editingFinished())); + connect(lineEdit, SIGNAL(returnPressed()), + this, SIGNAL(returnPressed())); + connect(lineEdit, SIGNAL(textChanged(QString)), + this, SLOT(setText(QString))); + + int iconsize = style()->pixelMetric(QStyle::PM_SmallIconSize); + QToolButton *clearButton = new QToolButton(this); + QIcon clearIcon = QIcon::fromTheme(QLatin1String("edit-clear"), + QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_clear.png"))); + clearButton->setIcon(clearIcon); + clearButton->setIconSize(QSize(iconsize, iconsize)); + clearButton->setFixedSize(QSize(iconsize, iconsize)); + clearButton->setStyleSheet("border: none;"); + clearButton->hide(); + connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); + + QToolButton *searchButton = new QToolButton(this); + QIcon searchIcon = QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier.png")); + searchButton->setIcon(searchIcon); + searchButton->setIconSize(QSize(iconsize, iconsize)); + searchButton->setFixedSize(QSize(iconsize, iconsize)); + searchButton->setStyleSheet("border: none;"); + searchButton->setPopupMode(QToolButton::InstantPopup); + searchButton->setEnabled(false); + connect(searchButton, SIGNAL(clicked()), this, SLOT(popupMenu())); + + pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton, searchButton); + + lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; } ") + .arg(pimpl->searchButtonPaddedWidth()) + .arg(pimpl->clearButtonPaddedWidth())); + const int width = qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth() + pimpl->searchButtonPaddedWidth()); + const int height = qMax(lineEdit->minimumSizeHint().height(), + qMax(pimpl->clearButtonPaddedHeight(), + pimpl->searchButtonPaddedHeight())); + lineEdit->setMinimumSize(width, height); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->addWidget(lineEdit); +} + +void QSearchField::setMenu(QMenu *menu) +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + pimpl->searchMenu = menu; + + QIcon searchIcon = menu ? QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier_menu.png")) + : QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier.png")); + pimpl->searchButton->setIcon(searchIcon); + pimpl->searchButton->setEnabled(isEnabled() && menu); +} + +void QSearchField::popupMenu() +{ + Q_ASSERT(pimpl); + if (!pimpl) + return; + + if (pimpl->searchMenu) { + const QRect screenRect = qApp->desktop()->availableGeometry(pimpl->searchButton); + const QSize sizeHint = pimpl->searchMenu->sizeHint(); + const QRect rect = pimpl->searchButton->rect(); + const int x = pimpl->searchButton->isRightToLeft() + ? rect.right() - sizeHint.width() + : rect.left(); + const int y = pimpl->searchButton->mapToGlobal(QPoint(0, rect.bottom())).y() + sizeHint.height() <= screenRect.height() + ? rect.bottom() + : rect.top() - sizeHint.height(); + QPoint point = pimpl->searchButton->mapToGlobal(QPoint(x, y)); + point.rx() = qMax(screenRect.left(), qMin(point.x(), screenRect.right() - sizeHint.width())); + point.ry() += 1; + + pimpl->searchMenu->popup(point); + } +} + +void QSearchField::changeEvent(QEvent* event) +{ + if (event->type() == QEvent::EnabledChange) { + Q_ASSERT(pimpl); + if (!pimpl) + return; + + const bool enabled = isEnabled(); + pimpl->searchButton->setEnabled(enabled && pimpl->searchMenu); + pimpl->lineEdit->setEnabled(enabled); + pimpl->clearButton->setEnabled(enabled); + } + QWidget::changeEvent(event); +} + +void QSearchField::setText(const QString &text) +{ + Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit); + if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) + return; + + pimpl->clearButton->setVisible(!text.isEmpty()); + + if (text != this->text()) + pimpl->lineEdit->setText(text); +} + +void QSearchField::setPlaceholderText(const QString &text) +{ + Q_ASSERT(pimpl && pimpl->lineEdit); + if (!(pimpl && pimpl->lineEdit)) + return; + +#if QT_VERSION >= 0x040700 + pimpl->lineEdit->setPlaceholderText(text); +#endif +} + +void QSearchField::clear() +{ + Q_ASSERT(pimpl && pimpl->lineEdit); + if (!(pimpl && pimpl->lineEdit)) + return; + + pimpl->lineEdit->clear(); +} + +void QSearchField::selectAll() +{ + Q_ASSERT(pimpl && pimpl->lineEdit); + if (!(pimpl && pimpl->lineEdit)) + return; + + pimpl->lineEdit->selectAll(); +} + +QString QSearchField::text() const +{ + Q_ASSERT(pimpl && pimpl->lineEdit); + if (!(pimpl && pimpl->lineEdit)) + return QString(); + + return pimpl->lineEdit->text(); +} + +QString QSearchField::placeholderText() const { + Q_ASSERT(pimpl && pimpl->lineEdit); + if (!(pimpl && pimpl->lineEdit)) + return QString(); + +#if QT_VERSION >= 0x040700 + return pimpl->lineEdit->placeholderText(); +#else + return QString(); +#endif +} + +void QSearchField::setFocus(Qt::FocusReason reason) +{ + Q_ASSERT(pimpl && pimpl->lineEdit); + if (pimpl && pimpl->lineEdit) + pimpl->lineEdit->setFocus(reason); +} + +void QSearchField::setFocus() +{ + setFocus(Qt::OtherFocusReason); +} + +void QSearchField::resizeEvent(QResizeEvent *resizeEvent) +{ + Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit); + if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) + return; + + QWidget::resizeEvent(resizeEvent); + const int x = width() - pimpl->clearButtonPaddedWidth(); + const int y = (height() - pimpl->clearButton->height())/2; + pimpl->clearButton->move(x, y); + + pimpl->searchButton->move(pimpl->lineEditFrameWidth() * 2, + (height() - pimpl->searchButton->height())/2); +} diff --git a/src/gui/qocoa/qsearchfield_nonmac.qrc b/src/gui/qocoa/qsearchfield_nonmac.qrc new file mode 100644 index 000000000..68b570d5b --- /dev/null +++ b/src/gui/qocoa/qsearchfield_nonmac.qrc @@ -0,0 +1,7 @@ + + + qsearchfield_nonmac_clear.png + qsearchfield_nonmac_magnifier_menu.png + qsearchfield_nonmac_magnifier.png + + diff --git a/src/gui/qocoa/qsearchfield_nonmac_clear.png b/src/gui/qocoa/qsearchfield_nonmac_clear.png new file mode 100644 index 0000000000000000000000000000000000000000..ec52c41bc2fbba0bcc2b30a96267d73be240dc13 GIT binary patch literal 736 zcmV<60w4W}P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igQ1 z5H}Q?5;Tqg00Ll1L_t(I%dL~KP8(SihQGOU@A%G)jYu&WzH`%nmEB~k%_)!0|LN%b|AGHm z-oG2$+uQ3>$}P|HIzosRKvh)}=iL2bvG{s(bCdlipx5iYRZ9ISrA(Dl2qAa@V2ok0 zSUehIK3`v7e|gpTKZ>>1@8dXrQ?J*FLsS(ar>K(E(Z z55w?XqtSR%uh-e%-)A2ykxBqT|~bUI}?98#7gQc7NC zTL2Qk_kDyA_`XjZ$7rp8@_S`v1>g5^&Mjw`8#w1C&N-~L#Bt1EFre9NlIJ;jp3`hL z84L!*ag4PV=N!(t$r4ai)xEVAV+`GHmo!ZokH_@;efs@ACb6=jZ2N{*lDHySr?2bF=h)KM8`M z@ne)p>7~2_ZUoUdwwqvaRgBnVYW{7Z(}7v%UkVf%q@6 SqLyO-0000Dhf?ouWA>yZ`s+BBx=){u((%TpyhhRjALHT2ckQc&d4&) z-n7Bm;H*K-pZ^kn7Mb{N&z!|jAk+T(YZimV`=g(K?qPLwkZ5DrlIwl{`;^p7iT5tI zx9yD4vv!!4s&o3#!*q*2WiQPZ0c-805^anh>)KKyITY=T68yi}@UJypmicbY)??rQ z+E&g>&)Y8jdRLLvT!xHUZR=iZbUpf-AmV!aZ=JhW=j&ZIa{LT^$CW)N?OE3SxI+54 wpr&f&y!tagmpaE@|F2cIdey48=Y>A7G=&6K*=^zp26~sl)78&qol`;+0Ic(R)c^nh literal 0 HcmV?d00001 diff --git a/src/gui/qocoa/qsearchfield_nonmac_magnifier_menu.png b/src/gui/qocoa/qsearchfield_nonmac_magnifier_menu.png new file mode 100644 index 0000000000000000000000000000000000000000..0e652c945e4287939e4787cd02c891c50238c64a GIT binary patch literal 439 zcmV;o0Z9IdP)e+J+88IQ+ODwU{IDgdn4Yj(RG-ELPU zlgZB|=kxg)CX$;ex$!IiEsZ@$z hOGL13`%eoV{s($5(uCrFlfnQ1002ovPDHLkV1gsuzk~n) literal 0 HcmV?d00001