├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Info.plist.in ├── LICENSE.txt ├── Qocoa.pro ├── README.md ├── TODO.md ├── gallery.cpp ├── gallery.h ├── gallery.png ├── main.cpp ├── qbutton.h ├── qbutton_mac.mm ├── qbutton_nonmac.cpp ├── qocoa_mac.h ├── qprogressindicatorspinning.h ├── qprogressindicatorspinning_mac.mm ├── qprogressindicatorspinning_nonmac.cpp ├── qprogressindicatorspinning_nonmac.gif ├── qprogressindicatorspinning_nonmac.qrc ├── qsearchfield.h ├── qsearchfield_mac.mm ├── qsearchfield_nonmac.cpp ├── qsearchfield_nonmac.qrc ├── qsearchfield_nonmac_clear.png ├── qsearchfield_nonmac_magnifier.png └── qsearchfield_nonmac_magnifier_menu.png /.gitignore: -------------------------------------------------------------------------------- 1 | cmake/ 2 | qmake/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | os: osx 3 | compiler: clang 4 | env: 5 | - XMAKE=qmake 6 | matrix: 7 | include: 8 | - compiler: clang 9 | env: 10 | - XMAKE=cmake 11 | - XMAKE_OPTIONS="." 12 | - compiler: clang 13 | env: 14 | - XMAKE=qmake 15 | - XMAKE_OPTIONS="NON_MAC_WIDGETS=1" 16 | - compiler: gcc 17 | env: 18 | - XMAKE=cmake 19 | - XMAKE_OPTIONS=". -DNON_MAC_WIDGETS=ON" 20 | - CC=gcc-4.9 21 | - CXX=g++-4.9 22 | before_install: 23 | - brew update 24 | install: 25 | - brew install qt 26 | script: 27 | - $XMAKE $XMAKE_OPTIONS && make 28 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Qocoa) 2 | cmake_minimum_required(VERSION 2.8) 3 | 4 | option(NON_MAC_WIDGETS "Force non-Mac widgets on OS X (useful for testing)" OFF) 5 | 6 | find_package(Qt4 COMPONENTS QtMain QtCore QtGui REQUIRED) 7 | include(UseQt4) 8 | 9 | set(SOURCES 10 | main.cpp 11 | gallery.cpp 12 | ) 13 | 14 | set(HEADERS 15 | gallery.h 16 | qsearchfield.h 17 | qbutton.h 18 | qprogressindicatorspinning.h 19 | ) 20 | 21 | qt4_wrap_cpp(MOC_SOURCES ${HEADERS}) 22 | 23 | if(APPLE AND NOT ${NON_MAC_WIDGETS}) 24 | list(APPEND SOURCES 25 | qsearchfield_mac.mm 26 | qbutton_mac.mm 27 | qprogressindicatorspinning_mac.mm 28 | ) 29 | else() 30 | message(here) 31 | list(APPEND SOURCES 32 | qsearchfield_nonmac.cpp 33 | qbutton_nonmac.cpp 34 | qprogressindicatorspinning_nonmac.cpp 35 | ) 36 | set(RESOURCES 37 | qsearchfield_nonmac.qrc 38 | qprogressindicatorspinning_nonmac.qrc 39 | ) 40 | qt4_add_resources(RESOURCES_SOURCES ${RESOURCES}) 41 | endif() 42 | 43 | add_executable(Qocoa 44 | WIN32 MACOSX_BUNDLE 45 | ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES} 46 | ) 47 | target_link_libraries(Qocoa ${QT_LIBRARIES}) 48 | 49 | if(APPLE AND NOT ${NON_MAC_WIDGETS}) 50 | set_target_properties(Qocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit") 51 | set_target_properties(Qocoa PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in) 52 | endif() 53 | -------------------------------------------------------------------------------- /Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | NSPrincipalClass 10 | NSApplication 11 | CFBundleDevelopmentRegion 12 | English 13 | CFBundleExecutable 14 | ${MACOSX_BUNDLE_EXECUTABLE_NAME} 15 | CFBundleGetInfoString 16 | ${MACOSX_BUNDLE_INFO_STRING} 17 | CFBundleIconFile 18 | ${MACOSX_BUNDLE_ICON_FILE} 19 | CFBundleIdentifier 20 | ${MACOSX_BUNDLE_GUI_IDENTIFIER} 21 | CFBundleInfoDictionaryVersion 22 | 6.0 23 | CFBundleLongVersionString 24 | ${MACOSX_BUNDLE_LONG_VERSION_STRING} 25 | CFBundleName 26 | ${MACOSX_BUNDLE_BUNDLE_NAME} 27 | CFBundlePackageType 28 | APPL 29 | CFBundleShortVersionString 30 | ${MACOSX_BUNDLE_SHORT_VERSION_STRING} 31 | CFBundleSignature 32 | ???? 33 | CFBundleVersion 34 | ${MACOSX_BUNDLE_BUNDLE_VERSION} 35 | CSResourcesFileMapped 36 | 37 | LSRequiresCarbon 38 | 39 | NSHumanReadableCopyright 40 | ${MACOSX_BUNDLE_COPYRIGHT} 41 | 42 | 43 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2011 by Mike McQuaid 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Qocoa.pro: -------------------------------------------------------------------------------- 1 | SOURCES += main.cpp\ 2 | gallery.cpp \ 3 | 4 | HEADERS += gallery.h \ 5 | qocoa_mac.h \ 6 | qsearchfield.h \ 7 | qbutton.h \ 8 | qprogressindicatorspinning.h \ 9 | 10 | !mac|equals(NON_MAC_WIDGETS, 1) { { 11 | SOURCES += qsearchfield_nonmac.cpp qbutton_nonmac.cpp qprogressindicatorspinning_nonmac.cpp 12 | RESOURCES += qsearchfield_nonmac.qrc qprogressindicatorspinning_nonmac.qrc 13 | } else { 14 | OBJECTIVE_SOURCES += qsearchfield_mac.mm qbutton_mac.mm qprogressindicatorspinning_mac.mm 15 | LIBS += -framework Foundation -framework Appkit 16 | QMAKE_CFLAGS += -mmacosx-version-min=10.6 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Qocoa 2 | Qocoa is a collection of Qt wrappers for OSX's Cocoa widgets. 3 | 4 | ## Features 5 | - basic fallback to sensible Qt types on non-OSX platforms 6 | - shared class headers which expose no implementation details 7 | - typical Qt signal/slot-based API 8 | - trivial to import into projects (class header/implementation, [single shared global header](https://github.com/mikemcquaid/Qocoa/blob/master/qocoa_mac.h)) 9 | 10 | ## Building 11 | ``` 12 | git clone git://github.com/mikemcquaid/Qocoa.git 13 | cd Qocoa 14 | qmake # or cmake . 15 | make 16 | ``` 17 | 18 | ## Status 19 | I'm not personally working on this any more but will accept pull-requests. 20 | 21 | [![Build Status](https://travis-ci.org/MikeMcQuaid/Qocoa.svg?branch=master)](https://travis-ci.org/MikeMcQuaid/Qocoa) 22 | 23 | ## Usage 24 | 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). 25 | 26 | ## Contact 27 | [Mike McQuaid](mailto:mike@mikemcquaid.com) 28 | 29 | ## License 30 | Qocoa is licensed under the [MIT License](http://en.wikipedia.org/wiki/MIT_License). 31 | The full license text is available in [LICENSE.txt](https://github.com/mikemcquaid/Qocoa/blob/master/LICENSE.txt). 32 | 33 | Magnifier and EditClear icons taken from [QtCreator](http://qt-project.org/) and are licensed under the [LGPL](http://www.gnu.org/copyleft/lesser.html). 34 | 35 | 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/). 36 | 37 | ## Gallery 38 | ![Qocoa Gallery](https://github.com/mikemcquaid/Qocoa/raw/master/gallery.png) 39 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | Widgets I hope to implement (or at least investigate): 2 | 3 | - NSTokenField 4 | - NSSegmentedControl 5 | - NSLevelIndicator 6 | - NSPathControl 7 | - NSSlider (Circular) 8 | - NSSplitView 9 | - NSTextFinder 10 | - NSOutlineView in an NSScrollView (Source List) 11 | - NSDrawer 12 | - PDFView 13 | - WebView 14 | -------------------------------------------------------------------------------- /gallery.cpp: -------------------------------------------------------------------------------- 1 | #include "gallery.h" 2 | 3 | #include 4 | 5 | #include "qsearchfield.h" 6 | #include "qbutton.h" 7 | #include "qprogressindicatorspinning.h" 8 | 9 | Gallery::Gallery(QWidget *parent) : QWidget(parent) 10 | { 11 | setWindowTitle("Qocoa Gallery"); 12 | QVBoxLayout *layout = new QVBoxLayout(this); 13 | 14 | QSearchField *searchField = new QSearchField(this); 15 | layout->addWidget(searchField); 16 | 17 | QSearchField *searchFieldPlaceholder = new QSearchField(this); 18 | searchFieldPlaceholder->setPlaceholderText("Placeholder text"); 19 | layout->addWidget(searchFieldPlaceholder); 20 | 21 | QButton *roundedButton = new QButton(this, QButton::Rounded); 22 | roundedButton->setText("Button"); 23 | layout->addWidget(roundedButton); 24 | 25 | QButton *regularSquareButton = new QButton(this, QButton::RegularSquare); 26 | regularSquareButton->setText("Button"); 27 | layout->addWidget(regularSquareButton); 28 | 29 | QButton *disclosureButton = new QButton(this, QButton::Disclosure); 30 | layout->addWidget(disclosureButton); 31 | 32 | QButton *shadowlessSquareButton = new QButton(this, QButton::ShadowlessSquare); 33 | shadowlessSquareButton->setText("Button"); 34 | layout->addWidget(shadowlessSquareButton); 35 | 36 | QButton *circularButton = new QButton(this, QButton::Circular); 37 | layout->addWidget(circularButton); 38 | 39 | QButton *textureSquareButton = new QButton(this, QButton::TexturedSquare); 40 | textureSquareButton->setText("Textured Button"); 41 | layout->addWidget(textureSquareButton); 42 | 43 | QButton *helpButton = new QButton(this, QButton::HelpButton); 44 | layout->addWidget(helpButton); 45 | 46 | QButton *smallSquareButton = new QButton(this, QButton::SmallSquare); 47 | smallSquareButton->setText("Gradient Button"); 48 | layout->addWidget(smallSquareButton); 49 | 50 | QButton *texturedRoundedButton = new QButton(this, QButton::TexturedRounded); 51 | texturedRoundedButton->setText("Round Textured"); 52 | layout->addWidget(texturedRoundedButton); 53 | 54 | QButton *roundedRectangleButton = new QButton(this, QButton::RoundRect); 55 | roundedRectangleButton->setText("Rounded Rect Button"); 56 | layout->addWidget(roundedRectangleButton); 57 | 58 | QButton *recessedButton = new QButton(this, QButton::Recessed); 59 | recessedButton->setText("Recessed Button"); 60 | layout->addWidget(recessedButton); 61 | 62 | QButton *roundedDisclosureButton = new QButton(this, QButton::RoundedDisclosure); 63 | layout->addWidget(roundedDisclosureButton); 64 | 65 | #ifdef __MAC_10_7 66 | QButton *inlineButton = new QButton(this, QButton::Inline); 67 | inlineButton->setText("Inline Button"); 68 | layout->addWidget(inlineButton); 69 | #endif 70 | 71 | 72 | QProgressIndicatorSpinning *progressIndicatorSpinning = new QProgressIndicatorSpinning(this); 73 | progressIndicatorSpinning->animate(); 74 | layout->addWidget(progressIndicatorSpinning); 75 | } 76 | -------------------------------------------------------------------------------- /gallery.h: -------------------------------------------------------------------------------- 1 | #ifndef GALLERY_H 2 | #define GALLERY_H 3 | 4 | #include 5 | 6 | class Gallery : public QWidget 7 | { 8 | Q_OBJECT 9 | 10 | public: 11 | explicit Gallery(QWidget *parent = 0); 12 | }; 13 | 14 | #endif // WIDGET_H 15 | -------------------------------------------------------------------------------- /gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/Qocoa/3a5623cb916e540ee46f5c13a85236ff458f4aba/gallery.png -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gallery.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QApplication application(argc, argv); 8 | 9 | Gallery gallery; 10 | gallery.show(); 11 | 12 | return application.exec(); 13 | } 14 | -------------------------------------------------------------------------------- /qbutton.h: -------------------------------------------------------------------------------- 1 | #ifndef QBUTTON_H 2 | #define QBUTTON_H 3 | 4 | #include 5 | #include 6 | 7 | class QButtonPrivate; 8 | class QButton : public QWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | // Matches NSBezelStyle 13 | enum BezelStyle { 14 | Rounded = 1, 15 | RegularSquare = 2, 16 | Disclosure = 5, 17 | ShadowlessSquare = 6, 18 | Circular = 7, 19 | TexturedSquare = 8, 20 | HelpButton = 9, 21 | SmallSquare = 10, 22 | TexturedRounded = 11, 23 | RoundRect = 12, 24 | Recessed = 13, 25 | RoundedDisclosure = 14, 26 | #ifdef __MAC_10_7 27 | Inline = 15 28 | #endif 29 | }; 30 | 31 | explicit QButton(QWidget *parent, BezelStyle bezelStyle = Rounded); 32 | 33 | public slots: 34 | void setText(const QString &text); 35 | void setImage(const QPixmap &image); 36 | void setChecked(bool checked); 37 | 38 | public: 39 | void setCheckable(bool checkable); 40 | bool isChecked(); 41 | 42 | signals: 43 | void clicked(bool checked = false); 44 | 45 | private: 46 | friend class QButtonPrivate; 47 | QPointer pimpl; 48 | }; 49 | #endif // QBUTTON_H 50 | -------------------------------------------------------------------------------- /qbutton_mac.mm: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qbutton.h" 24 | 25 | #include "qocoa_mac.h" 26 | 27 | #import "Foundation/NSAutoreleasePool.h" 28 | #import "AppKit/NSButton.h" 29 | #import "AppKit/NSFont.h" 30 | 31 | class QButtonPrivate : public QObject 32 | { 33 | public: 34 | QButtonPrivate(QButton *qButton, NSButton *nsButton, QButton::BezelStyle bezelStyle) 35 | : QObject(qButton), qButton(qButton), nsButton(nsButton) 36 | { 37 | switch(bezelStyle) { 38 | case QButton::Disclosure: 39 | case QButton::Circular: 40 | #ifdef __MAC_10_7 41 | case QButton::Inline: 42 | #endif 43 | case QButton::RoundedDisclosure: 44 | case QButton::HelpButton: 45 | [nsButton setTitle:@""]; 46 | default: 47 | break; 48 | } 49 | 50 | NSFont* font = 0; 51 | switch(bezelStyle) { 52 | case QButton::RoundRect: 53 | font = [NSFont fontWithName:@"Lucida Grande" size:12]; 54 | break; 55 | 56 | case QButton::Recessed: 57 | font = [NSFont fontWithName:@"Lucida Grande Bold" size:12]; 58 | break; 59 | 60 | #ifdef __MAC_10_7 61 | case QButton::Inline: 62 | font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]; 63 | break; 64 | #endif 65 | 66 | default: 67 | font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]]; 68 | break; 69 | } 70 | [nsButton setFont:font]; 71 | 72 | switch(bezelStyle) { 73 | case QButton::Rounded: 74 | qButton->setMinimumWidth(40); 75 | qButton->setFixedHeight(24); 76 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 77 | break; 78 | case QButton::RegularSquare: 79 | case QButton::TexturedSquare: 80 | qButton->setMinimumSize(14, 23); 81 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 82 | break; 83 | case QButton::ShadowlessSquare: 84 | qButton->setMinimumSize(5, 25); 85 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 86 | break; 87 | case QButton::SmallSquare: 88 | qButton->setMinimumSize(4, 21); 89 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 90 | break; 91 | case QButton::TexturedRounded: 92 | qButton->setMinimumSize(10, 22); 93 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 94 | break; 95 | case QButton::RoundRect: 96 | case QButton::Recessed: 97 | qButton->setMinimumWidth(16); 98 | qButton->setFixedHeight(18); 99 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 100 | break; 101 | case QButton::Disclosure: 102 | qButton->setMinimumWidth(13); 103 | qButton->setFixedHeight(13); 104 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 105 | break; 106 | case QButton::Circular: 107 | qButton->setMinimumSize(16, 16); 108 | qButton->setMaximumHeight(40); 109 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); 110 | break; 111 | case QButton::HelpButton: 112 | case QButton::RoundedDisclosure: 113 | qButton->setMinimumWidth(22); 114 | qButton->setFixedHeight(22); 115 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 116 | break; 117 | #ifdef __MAC_10_7 118 | case QButton::Inline: 119 | qButton->setMinimumWidth(10); 120 | qButton->setFixedHeight(16); 121 | qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); 122 | break; 123 | #endif 124 | } 125 | 126 | switch(bezelStyle) { 127 | case QButton::Recessed: 128 | [nsButton setButtonType:NSPushOnPushOffButton]; 129 | case QButton::Disclosure: 130 | [nsButton setButtonType:NSOnOffButton]; 131 | default: 132 | [nsButton setButtonType:NSMomentaryPushInButton]; 133 | } 134 | 135 | [nsButton setBezelStyle:bezelStyle]; 136 | } 137 | 138 | void clicked() 139 | { 140 | emit qButton->clicked(qButton->isChecked()); 141 | } 142 | 143 | ~QButtonPrivate() { 144 | [[nsButton target] release]; 145 | [nsButton setTarget:nil]; 146 | } 147 | 148 | QButton *qButton; 149 | NSButton *nsButton; 150 | }; 151 | 152 | @interface QButtonTarget : NSObject 153 | { 154 | @public 155 | QPointer pimpl; 156 | } 157 | -(void)clicked; 158 | @end 159 | 160 | @implementation QButtonTarget 161 | -(void)clicked { 162 | Q_ASSERT(pimpl); 163 | if (pimpl) 164 | pimpl->clicked(); 165 | } 166 | @end 167 | 168 | QButton::QButton(QWidget *parent, BezelStyle bezelStyle) : QWidget(parent) 169 | { 170 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 171 | 172 | NSButton *button = [[NSButton alloc] init]; 173 | pimpl = new QButtonPrivate(this, button, bezelStyle); 174 | 175 | QButtonTarget *target = [[QButtonTarget alloc] init]; 176 | target->pimpl = pimpl; 177 | [button setTarget:target]; 178 | 179 | [button setAction:@selector(clicked)]; 180 | 181 | setupLayout(button, this); 182 | 183 | [button release]; 184 | 185 | [pool drain]; 186 | } 187 | 188 | void QButton::setText(const QString &text) 189 | { 190 | Q_ASSERT(pimpl); 191 | if (!pimpl) 192 | return; 193 | 194 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 195 | [pimpl->nsButton setTitle:fromQString(text)]; 196 | [pool drain]; 197 | } 198 | 199 | void QButton::setImage(const QPixmap &image) 200 | { 201 | Q_ASSERT(pimpl); 202 | if (pimpl) 203 | [pimpl->nsButton setImage:fromQPixmap(image)]; 204 | } 205 | 206 | void QButton::setChecked(bool checked) 207 | { 208 | Q_ASSERT(pimpl); 209 | if (pimpl) 210 | [pimpl->nsButton setState:checked]; 211 | } 212 | 213 | void QButton::setCheckable(bool checkable) 214 | { 215 | const NSInteger cellMask = checkable ? NSChangeBackgroundCellMask : NSNoCellMask; 216 | 217 | Q_ASSERT(pimpl); 218 | if (pimpl) 219 | [[pimpl->nsButton cell] setShowsStateBy:cellMask]; 220 | } 221 | 222 | bool QButton::isChecked() 223 | { 224 | Q_ASSERT(pimpl); 225 | if (!pimpl) 226 | return false; 227 | 228 | return [pimpl->nsButton state]; 229 | } 230 | -------------------------------------------------------------------------------- /qbutton_nonmac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qbutton.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class QButtonPrivate : public QObject 31 | { 32 | public: 33 | QButtonPrivate(QButton *button, QAbstractButton *abstractButton) 34 | : QObject(button), abstractButton(abstractButton) {} 35 | QPointer abstractButton; 36 | }; 37 | 38 | QButton::QButton(QWidget *parent, BezelStyle) : QWidget(parent) 39 | { 40 | QAbstractButton *button = 0; 41 | if (qobject_cast(parent)) 42 | button = new QToolButton(this); 43 | else 44 | button = new QPushButton(this); 45 | connect(button, SIGNAL(clicked()), 46 | this, SIGNAL(clicked())); 47 | pimpl = new QButtonPrivate(this, button); 48 | 49 | QVBoxLayout *layout = new QVBoxLayout(this); 50 | layout->setMargin(0); 51 | layout->addWidget(button); 52 | } 53 | 54 | void QButton::setText(const QString &text) 55 | { 56 | Q_ASSERT(pimpl); 57 | if (pimpl) 58 | pimpl->abstractButton->setText(text); 59 | } 60 | 61 | void QButton::setImage(const QPixmap &image) 62 | { 63 | Q_ASSERT(pimpl); 64 | if (pimpl) 65 | pimpl->abstractButton->setIcon(image); 66 | } 67 | 68 | void QButton::setChecked(bool checked) 69 | { 70 | Q_ASSERT(pimpl); 71 | if (pimpl) 72 | pimpl->abstractButton->setChecked(checked); 73 | } 74 | 75 | void QButton::setCheckable(bool checkable) 76 | { 77 | Q_ASSERT(pimpl); 78 | if (pimpl) 79 | pimpl->abstractButton->setCheckable(checkable); 80 | } 81 | 82 | bool QButton::isChecked() 83 | { 84 | Q_ASSERT(pimpl); 85 | if (!pimpl) 86 | return false; 87 | 88 | return pimpl->abstractButton->isChecked(); 89 | } 90 | -------------------------------------------------------------------------------- /qocoa_mac.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #if QT_VERSION >= QT_VERSION_CHECK(5,0,0) 30 | #include 31 | #endif 32 | 33 | static inline NSString* fromQString(const QString &string) 34 | { 35 | const QByteArray utf8 = string.toUtf8(); 36 | const char* cString = utf8.constData(); 37 | return [[NSString alloc] initWithUTF8String:cString]; 38 | } 39 | 40 | static inline QString toQString(NSString *string) 41 | { 42 | if (!string) 43 | return QString(); 44 | return QString::fromUtf8([string UTF8String]); 45 | } 46 | 47 | static inline NSImage* fromQPixmap(const QPixmap &pixmap) 48 | { 49 | #if QT_VERSION < QT_VERSION_CHECK(5,0,0) 50 | CGImageRef cgImage = pixmap.toMacCGImageRef(); 51 | #else 52 | CGImageRef cgImage = QtMac::toCGImageRef(pixmap); 53 | #endif 54 | return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; 55 | } 56 | 57 | static inline void setupLayout(NSView *cocoaView, QWidget *parent) 58 | { 59 | parent->setAttribute(Qt::WA_NativeWindow); 60 | QVBoxLayout *layout = new QVBoxLayout(parent); 61 | layout->setMargin(0); 62 | layout->addWidget(new QMacCocoaViewContainer(cocoaView, parent)); 63 | } 64 | -------------------------------------------------------------------------------- /qprogressindicatorspinning.h: -------------------------------------------------------------------------------- 1 | #ifndef QPROGRESSINDICATORSPINNING_H 2 | #define QPROGRESSINDICATORSPINNING_H 3 | 4 | #include 5 | #include 6 | 7 | class QProgressIndicatorSpinningPrivate; 8 | class QProgressIndicatorSpinning : public QWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | // Matches NSProgressIndicatorThickness 13 | enum Thickness { 14 | Default = 14, 15 | Small = 10, 16 | Large = 18, 17 | Aqua = 12 18 | }; 19 | 20 | explicit QProgressIndicatorSpinning(QWidget *parent, 21 | Thickness thickness = Default); 22 | public slots: 23 | void animate(bool animate = true); 24 | private: 25 | friend class QProgressIndicatorSpinningPrivate; 26 | QPointer pimpl; 27 | }; 28 | 29 | #endif // QPROGRESSINDICATORSPINNING_H 30 | -------------------------------------------------------------------------------- /qprogressindicatorspinning_mac.mm: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qprogressindicatorspinning.h" 24 | 25 | #include "qocoa_mac.h" 26 | 27 | #import "Foundation/NSAutoreleasePool.h" 28 | #import "AppKit/NSProgressIndicator.h" 29 | 30 | class QProgressIndicatorSpinningPrivate : public QObject 31 | { 32 | public: 33 | QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning, 34 | NSProgressIndicator *nsProgressIndicator) 35 | : QObject(qProgressIndicatorSpinning), nsProgressIndicator(nsProgressIndicator) {} 36 | 37 | NSProgressIndicator *nsProgressIndicator; 38 | }; 39 | 40 | QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent, 41 | Thickness thickness) 42 | : QWidget(parent) 43 | { 44 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 45 | 46 | NSProgressIndicator *progress = [[NSProgressIndicator alloc] init]; 47 | [progress setStyle:NSProgressIndicatorSpinningStyle]; 48 | 49 | pimpl = new QProgressIndicatorSpinningPrivate(this, progress); 50 | 51 | setupLayout(progress, this); 52 | 53 | setFixedSize(thickness, thickness); 54 | 55 | [progress release]; 56 | 57 | [pool drain]; 58 | } 59 | 60 | void QProgressIndicatorSpinning::animate(bool animate) 61 | { 62 | Q_ASSERT(pimpl); 63 | if (!pimpl) 64 | return; 65 | 66 | if (animate) 67 | [pimpl->nsProgressIndicator startAnimation:nil]; 68 | else 69 | [pimpl->nsProgressIndicator stopAnimation:nil]; 70 | } 71 | -------------------------------------------------------------------------------- /qprogressindicatorspinning_nonmac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qprogressindicatorspinning.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | class QProgressIndicatorSpinningPrivate : public QObject 30 | { 31 | public: 32 | QProgressIndicatorSpinningPrivate(QProgressIndicatorSpinning *qProgressIndicatorSpinning, 33 | QMovie *movie) 34 | : QObject(qProgressIndicatorSpinning), movie(movie) {} 35 | 36 | QPointer movie; 37 | }; 38 | 39 | QProgressIndicatorSpinning::QProgressIndicatorSpinning(QWidget *parent, 40 | Thickness thickness) 41 | : QWidget(parent) 42 | { 43 | QVBoxLayout *layout = new QVBoxLayout(this); 44 | layout->setMargin(0); 45 | 46 | QSize size(thickness, thickness); 47 | QMovie *movie = new QMovie(this); 48 | movie->setFileName(":/Qocoa/qprogressindicatorspinning_nonmac.gif"); 49 | movie->setScaledSize(size); 50 | // Roughly match OSX speed. 51 | movie->setSpeed(200); 52 | pimpl = new QProgressIndicatorSpinningPrivate(this, movie); 53 | 54 | QLabel *label = new QLabel(this); 55 | label->setMovie(movie); 56 | 57 | layout->addWidget(label); 58 | setFixedSize(size); 59 | } 60 | 61 | 62 | void QProgressIndicatorSpinning::animate(bool animate) 63 | { 64 | Q_ASSERT(pimpl && pimpl->movie); 65 | if (!(pimpl && pimpl->movie)) 66 | return; 67 | 68 | if (animate) 69 | pimpl->movie->start(); 70 | else 71 | pimpl->movie->stop(); 72 | } 73 | -------------------------------------------------------------------------------- /qprogressindicatorspinning_nonmac.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/Qocoa/3a5623cb916e540ee46f5c13a85236ff458f4aba/qprogressindicatorspinning_nonmac.gif -------------------------------------------------------------------------------- /qprogressindicatorspinning_nonmac.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qprogressindicatorspinning_nonmac.gif 4 | 5 | 6 | -------------------------------------------------------------------------------- /qsearchfield.h: -------------------------------------------------------------------------------- 1 | #ifndef QSEARCHFIELD_H 2 | #define QSEARCHFIELD_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class QSearchFieldPrivate; 9 | class QSearchField : public QWidget 10 | { 11 | Q_OBJECT 12 | 13 | Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged USER true); 14 | Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText); 15 | 16 | public: 17 | explicit QSearchField(QWidget *parent); 18 | 19 | QString text() const; 20 | QString placeholderText() const; 21 | void setFocus(Qt::FocusReason); 22 | void setMenu(QMenu *menu); 23 | 24 | public slots: 25 | void setText(const QString &text); 26 | void setPlaceholderText(const QString &text); 27 | void clear(); 28 | void selectAll(); 29 | void setFocus(); 30 | 31 | signals: 32 | void textChanged(const QString &text); 33 | void editingFinished(); 34 | void returnPressed(); 35 | 36 | private slots: 37 | void popupMenu(); 38 | 39 | protected: 40 | void changeEvent(QEvent*); 41 | void resizeEvent(QResizeEvent*); 42 | 43 | private: 44 | friend class QSearchFieldPrivate; 45 | QPointer pimpl; 46 | }; 47 | 48 | #endif // QSEARCHFIELD_H 49 | -------------------------------------------------------------------------------- /qsearchfield_mac.mm: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qsearchfield.h" 24 | 25 | #include "qocoa_mac.h" 26 | 27 | #import "Foundation/NSAutoreleasePool.h" 28 | #import "Foundation/NSNotification.h" 29 | #import "AppKit/NSSearchField.h" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | class QSearchFieldPrivate : public QObject 36 | { 37 | public: 38 | QSearchFieldPrivate(QSearchField *qSearchField, NSSearchField *nsSearchField) 39 | : QObject(qSearchField), qSearchField(qSearchField), nsSearchField(nsSearchField) {} 40 | 41 | void textDidChange(const QString &text) 42 | { 43 | if (qSearchField) 44 | emit qSearchField->textChanged(text); 45 | } 46 | 47 | void textDidEndEditing() 48 | { 49 | if (qSearchField) 50 | emit qSearchField->editingFinished(); 51 | } 52 | 53 | void returnPressed() 54 | { 55 | if (qSearchField) { 56 | emit qSearchField->returnPressed(); 57 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier); 58 | QApplication::postEvent(qSearchField, event); 59 | } 60 | } 61 | 62 | void keyDownPressed() 63 | { 64 | if (qSearchField) { 65 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Down, Qt::NoModifier); 66 | QApplication::postEvent(qSearchField, event); 67 | } 68 | } 69 | 70 | void keyUpPressed() 71 | { 72 | if (qSearchField) { 73 | QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Up, Qt::NoModifier); 74 | QApplication::postEvent(qSearchField, event); 75 | } 76 | } 77 | 78 | QPointer qSearchField; 79 | NSSearchField *nsSearchField; 80 | }; 81 | 82 | @interface QSearchFieldDelegate : NSObject 83 | { 84 | @public 85 | QPointer pimpl; 86 | } 87 | -(void)controlTextDidChange:(NSNotification*)notification; 88 | -(void)controlTextDidEndEditing:(NSNotification*)notification; 89 | @end 90 | 91 | @implementation QSearchFieldDelegate 92 | -(void)controlTextDidChange:(NSNotification*)notification { 93 | Q_ASSERT(pimpl); 94 | if (pimpl) 95 | pimpl->textDidChange(toQString([[notification object] stringValue])); 96 | } 97 | 98 | -(void)controlTextDidEndEditing:(NSNotification*)notification { 99 | Q_UNUSED(notification); 100 | // No Q_ASSERT here as it is called on destruction. 101 | if (pimpl) 102 | pimpl->textDidEndEditing(); 103 | 104 | if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement) 105 | pimpl->returnPressed(); 106 | } 107 | 108 | -(BOOL)control: (NSControl *)control textView: 109 | (NSTextView *)textView doCommandBySelector: 110 | (SEL)commandSelector { 111 | Q_ASSERT(pimpl); 112 | if (!pimpl) return NO; 113 | 114 | if (commandSelector == @selector(moveDown:)) { 115 | pimpl->keyDownPressed(); 116 | return YES; 117 | } else if (commandSelector == @selector(moveUp:)) { 118 | pimpl->keyUpPressed(); 119 | return YES; 120 | } 121 | return NO; 122 | } 123 | 124 | @end 125 | 126 | @interface QocoaSearchField : NSSearchField 127 | -(BOOL)performKeyEquivalent:(NSEvent*)event; 128 | @end 129 | 130 | @implementation QocoaSearchField 131 | -(BOOL)performKeyEquivalent:(NSEvent*)event { 132 | // First, check if we have the focus. 133 | // If no, it probably means this event isn't for us. 134 | NSResponder* firstResponder = [[NSApp keyWindow] firstResponder]; 135 | if ([firstResponder isKindOfClass:[NSText class]] && 136 | [(NSText*)firstResponder delegate] == self) { 137 | 138 | if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask) 139 | { 140 | QString keyString = toQString([event characters]); 141 | if (keyString == "a") // Cmd+a 142 | { 143 | [self performSelector:@selector(selectText:)]; 144 | return YES; 145 | } 146 | else if (keyString == "c") // Cmd+c 147 | { 148 | [[self currentEditor] copy: nil]; 149 | return YES; 150 | } 151 | else if (keyString == "v") // Cmd+v 152 | { 153 | [[self currentEditor] paste: nil]; 154 | return YES; 155 | } 156 | else if (keyString == "x") // Cmd+x 157 | { 158 | [[self currentEditor] cut: nil]; 159 | return YES; 160 | } 161 | } 162 | } 163 | 164 | return NO; 165 | } 166 | @end 167 | 168 | QSearchField::QSearchField(QWidget *parent) : QWidget(parent) 169 | { 170 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 171 | 172 | NSSearchField *search = [[QocoaSearchField alloc] init]; 173 | 174 | QSearchFieldDelegate *delegate = [[QSearchFieldDelegate alloc] init]; 175 | pimpl = delegate->pimpl = new QSearchFieldPrivate(this, search); 176 | [search setDelegate:delegate]; 177 | 178 | setupLayout(search, this); 179 | 180 | setFixedHeight(24); 181 | setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); 182 | 183 | [search release]; 184 | 185 | [pool drain]; 186 | } 187 | 188 | void QSearchField::setMenu(QMenu *menu) 189 | { 190 | Q_ASSERT(pimpl); 191 | if (!pimpl) 192 | return; 193 | 194 | #if QT_VERSION < QT_VERSION_CHECK(5,0,0) 195 | NSMenu *nsMenu = menu->macMenu(); 196 | #else 197 | NSMenu *nsMenu = menu->toNSMenu(); 198 | #endif 199 | 200 | [[pimpl->nsSearchField cell] setSearchMenuTemplate:nsMenu]; 201 | } 202 | 203 | void QSearchField::popupMenu() 204 | { 205 | } 206 | 207 | void QSearchField::setText(const QString &text) 208 | { 209 | Q_ASSERT(pimpl); 210 | if (!pimpl) 211 | return; 212 | 213 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 214 | [pimpl->nsSearchField setStringValue:fromQString(text)]; 215 | [pool drain]; 216 | } 217 | 218 | void QSearchField::setPlaceholderText(const QString &text) 219 | { 220 | Q_ASSERT(pimpl); 221 | if (!pimpl) 222 | return; 223 | 224 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 225 | [[pimpl->nsSearchField cell] setPlaceholderString:fromQString(text)]; 226 | [pool drain]; 227 | } 228 | 229 | void QSearchField::clear() 230 | { 231 | Q_ASSERT(pimpl); 232 | if (!pimpl) 233 | return; 234 | 235 | [pimpl->nsSearchField setStringValue:@""]; 236 | emit textChanged(QString()); 237 | } 238 | 239 | void QSearchField::selectAll() 240 | { 241 | Q_ASSERT(pimpl); 242 | if (!pimpl) 243 | return; 244 | 245 | [pimpl->nsSearchField performSelector:@selector(selectText:)]; 246 | } 247 | 248 | QString QSearchField::text() const 249 | { 250 | Q_ASSERT(pimpl); 251 | if (!pimpl) 252 | return QString(); 253 | 254 | return toQString([pimpl->nsSearchField stringValue]); 255 | } 256 | 257 | QString QSearchField::placeholderText() const 258 | { 259 | Q_ASSERT(pimpl); 260 | if (!pimpl) 261 | return QString(); 262 | 263 | return toQString([[pimpl->nsSearchField cell] placeholderString]); 264 | } 265 | 266 | void QSearchField::setFocus(Qt::FocusReason) 267 | { 268 | Q_ASSERT(pimpl); 269 | if (!pimpl) 270 | return; 271 | 272 | if ([pimpl->nsSearchField acceptsFirstResponder]) 273 | [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField]; 274 | } 275 | 276 | void QSearchField::setFocus() 277 | { 278 | setFocus(Qt::OtherFocusReason); 279 | } 280 | 281 | void QSearchField::changeEvent(QEvent* event) 282 | { 283 | if (event->type() == QEvent::EnabledChange) { 284 | Q_ASSERT(pimpl); 285 | if (!pimpl) 286 | return; 287 | 288 | const bool enabled = isEnabled(); 289 | [pimpl->nsSearchField setEnabled: enabled]; 290 | } 291 | QWidget::changeEvent(event); 292 | } 293 | 294 | void QSearchField::resizeEvent(QResizeEvent *resizeEvent) 295 | { 296 | QWidget::resizeEvent(resizeEvent); 297 | } 298 | -------------------------------------------------------------------------------- /qsearchfield_nonmac.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Mike McQuaid 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "qsearchfield.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | 35 | class QSearchFieldPrivate : public QObject 36 | { 37 | public: 38 | QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton, QToolButton *searchButton) 39 | : QObject(searchField), lineEdit(lineEdit), clearButton(clearButton), searchButton(searchButton) {} 40 | 41 | int lineEditFrameWidth() const { 42 | return lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); 43 | } 44 | 45 | int clearButtonPaddedWidth() const { 46 | return clearButton->width() + lineEditFrameWidth() * 2; 47 | } 48 | 49 | int clearButtonPaddedHeight() const { 50 | return clearButton->height() + lineEditFrameWidth() * 2; 51 | } 52 | 53 | int searchButtonPaddedWidth() const { 54 | return searchButton->width() + lineEditFrameWidth() * 2; 55 | } 56 | 57 | int searchButtonPaddedHeight() const { 58 | return searchButton->height() + lineEditFrameWidth() * 2; 59 | } 60 | 61 | QPointer lineEdit; 62 | QPointer clearButton; 63 | QPointer searchButton; 64 | QPointer searchMenu; 65 | }; 66 | 67 | QSearchField::QSearchField(QWidget *parent) : QWidget(parent) 68 | { 69 | QLineEdit *lineEdit = new QLineEdit(this); 70 | connect(lineEdit, SIGNAL(textChanged(QString)), 71 | this, SIGNAL(textChanged(QString))); 72 | connect(lineEdit, SIGNAL(editingFinished()), 73 | this, SIGNAL(editingFinished())); 74 | connect(lineEdit, SIGNAL(returnPressed()), 75 | this, SIGNAL(returnPressed())); 76 | connect(lineEdit, SIGNAL(textChanged(QString)), 77 | this, SLOT(setText(QString))); 78 | 79 | int iconsize = style()->pixelMetric(QStyle::PM_SmallIconSize); 80 | QToolButton *clearButton = new QToolButton(this); 81 | QIcon clearIcon = QIcon::fromTheme(QLatin1String("edit-clear"), 82 | QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_clear.png"))); 83 | clearButton->setIcon(clearIcon); 84 | clearButton->setIconSize(QSize(iconsize, iconsize)); 85 | clearButton->setFixedSize(QSize(iconsize, iconsize)); 86 | clearButton->setStyleSheet("border: none;"); 87 | clearButton->hide(); 88 | connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); 89 | 90 | QToolButton *searchButton = new QToolButton(this); 91 | QIcon searchIcon = QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier.png")); 92 | searchButton->setIcon(searchIcon); 93 | searchButton->setIconSize(QSize(iconsize, iconsize)); 94 | searchButton->setFixedSize(QSize(iconsize, iconsize)); 95 | searchButton->setStyleSheet("border: none;"); 96 | searchButton->setPopupMode(QToolButton::InstantPopup); 97 | searchButton->setEnabled(false); 98 | connect(searchButton, SIGNAL(clicked()), this, SLOT(popupMenu())); 99 | 100 | pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton, searchButton); 101 | 102 | lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; padding-right: %2px; } ") 103 | .arg(pimpl->searchButtonPaddedWidth()) 104 | .arg(pimpl->clearButtonPaddedWidth())); 105 | const int width = qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth() + pimpl->searchButtonPaddedWidth()); 106 | const int height = qMax(lineEdit->minimumSizeHint().height(), 107 | qMax(pimpl->clearButtonPaddedHeight(), 108 | pimpl->searchButtonPaddedHeight())); 109 | lineEdit->setMinimumSize(width, height); 110 | 111 | QVBoxLayout *layout = new QVBoxLayout(this); 112 | layout->setMargin(0); 113 | layout->addWidget(lineEdit); 114 | } 115 | 116 | void QSearchField::setMenu(QMenu *menu) 117 | { 118 | Q_ASSERT(pimpl); 119 | if (!pimpl) 120 | return; 121 | 122 | pimpl->searchMenu = menu; 123 | 124 | QIcon searchIcon = menu ? QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier_menu.png")) 125 | : QIcon(QLatin1String(":/Qocoa/qsearchfield_nonmac_magnifier.png")); 126 | pimpl->searchButton->setIcon(searchIcon); 127 | pimpl->searchButton->setEnabled(isEnabled() && menu); 128 | } 129 | 130 | void QSearchField::popupMenu() 131 | { 132 | Q_ASSERT(pimpl); 133 | if (!pimpl) 134 | return; 135 | 136 | if (pimpl->searchMenu) { 137 | const QRect screenRect = qApp->desktop()->availableGeometry(pimpl->searchButton); 138 | const QSize sizeHint = pimpl->searchMenu->sizeHint(); 139 | const QRect rect = pimpl->searchButton->rect(); 140 | const int x = pimpl->searchButton->isRightToLeft() 141 | ? rect.right() - sizeHint.width() 142 | : rect.left(); 143 | const int y = pimpl->searchButton->mapToGlobal(QPoint(0, rect.bottom())).y() + sizeHint.height() <= screenRect.height() 144 | ? rect.bottom() 145 | : rect.top() - sizeHint.height(); 146 | QPoint point = pimpl->searchButton->mapToGlobal(QPoint(x, y)); 147 | point.rx() = qMax(screenRect.left(), qMin(point.x(), screenRect.right() - sizeHint.width())); 148 | point.ry() += 1; 149 | 150 | pimpl->searchMenu->popup(point); 151 | } 152 | } 153 | 154 | void QSearchField::changeEvent(QEvent* event) 155 | { 156 | if (event->type() == QEvent::EnabledChange) { 157 | Q_ASSERT(pimpl); 158 | if (!pimpl) 159 | return; 160 | 161 | const bool enabled = isEnabled(); 162 | pimpl->searchButton->setEnabled(enabled && pimpl->searchMenu); 163 | pimpl->lineEdit->setEnabled(enabled); 164 | pimpl->clearButton->setEnabled(enabled); 165 | } 166 | QWidget::changeEvent(event); 167 | } 168 | 169 | void QSearchField::setText(const QString &text) 170 | { 171 | Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit); 172 | if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) 173 | return; 174 | 175 | pimpl->clearButton->setVisible(!text.isEmpty()); 176 | 177 | if (text != this->text()) 178 | pimpl->lineEdit->setText(text); 179 | } 180 | 181 | void QSearchField::setPlaceholderText(const QString &text) 182 | { 183 | Q_ASSERT(pimpl && pimpl->lineEdit); 184 | if (!(pimpl && pimpl->lineEdit)) 185 | return; 186 | 187 | #if QT_VERSION >= 0x040700 188 | pimpl->lineEdit->setPlaceholderText(text); 189 | #endif 190 | } 191 | 192 | void QSearchField::clear() 193 | { 194 | Q_ASSERT(pimpl && pimpl->lineEdit); 195 | if (!(pimpl && pimpl->lineEdit)) 196 | return; 197 | 198 | pimpl->lineEdit->clear(); 199 | } 200 | 201 | void QSearchField::selectAll() 202 | { 203 | Q_ASSERT(pimpl && pimpl->lineEdit); 204 | if (!(pimpl && pimpl->lineEdit)) 205 | return; 206 | 207 | pimpl->lineEdit->selectAll(); 208 | } 209 | 210 | QString QSearchField::text() const 211 | { 212 | Q_ASSERT(pimpl && pimpl->lineEdit); 213 | if (!(pimpl && pimpl->lineEdit)) 214 | return QString(); 215 | 216 | return pimpl->lineEdit->text(); 217 | } 218 | 219 | QString QSearchField::placeholderText() const { 220 | Q_ASSERT(pimpl && pimpl->lineEdit); 221 | if (!(pimpl && pimpl->lineEdit)) 222 | return QString(); 223 | 224 | #if QT_VERSION >= 0x040700 225 | return pimpl->lineEdit->placeholderText(); 226 | #else 227 | return QString(); 228 | #endif 229 | } 230 | 231 | void QSearchField::setFocus(Qt::FocusReason reason) 232 | { 233 | Q_ASSERT(pimpl && pimpl->lineEdit); 234 | if (pimpl && pimpl->lineEdit) 235 | pimpl->lineEdit->setFocus(reason); 236 | } 237 | 238 | void QSearchField::setFocus() 239 | { 240 | setFocus(Qt::OtherFocusReason); 241 | } 242 | 243 | void QSearchField::resizeEvent(QResizeEvent *resizeEvent) 244 | { 245 | Q_ASSERT(pimpl && pimpl->clearButton && pimpl->lineEdit); 246 | if (!(pimpl && pimpl->clearButton && pimpl->lineEdit)) 247 | return; 248 | 249 | QWidget::resizeEvent(resizeEvent); 250 | const int x = width() - pimpl->clearButtonPaddedWidth(); 251 | const int y = (height() - pimpl->clearButton->height())/2; 252 | pimpl->clearButton->move(x, y); 253 | 254 | pimpl->searchButton->move(pimpl->lineEditFrameWidth() * 2, 255 | (height() - pimpl->searchButton->height())/2); 256 | } 257 | -------------------------------------------------------------------------------- /qsearchfield_nonmac.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | qsearchfield_nonmac_clear.png 4 | qsearchfield_nonmac_magnifier_menu.png 5 | qsearchfield_nonmac_magnifier.png 6 | 7 | 8 | -------------------------------------------------------------------------------- /qsearchfield_nonmac_clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/Qocoa/3a5623cb916e540ee46f5c13a85236ff458f4aba/qsearchfield_nonmac_clear.png -------------------------------------------------------------------------------- /qsearchfield_nonmac_magnifier.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/Qocoa/3a5623cb916e540ee46f5c13a85236ff458f4aba/qsearchfield_nonmac_magnifier.png -------------------------------------------------------------------------------- /qsearchfield_nonmac_magnifier_menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MikeMcQuaid/Qocoa/3a5623cb916e540ee46f5c13a85236ff458f4aba/qsearchfield_nonmac_magnifier_menu.png --------------------------------------------------------------------------------