Fix macos build

This commit is contained in:
Jonas Kvinge
2018-07-01 22:26:46 +02:00
parent c4e75dea65
commit ada7325a04
51 changed files with 453 additions and 399 deletions

View File

@@ -24,6 +24,7 @@ else()
qprogressindicatorspinning_nonmac.cpp
)
set(RESOURCES
qsearchfield_nonmac.qrc
qprogressindicatorspinning_nonmac.qrc
)
qt5_add_resources(RESOURCES_SOURCES ${RESOURCES})
@@ -31,3 +32,4 @@ endif()
add_library(Qocoa STATIC ${SOURCES} ${MOC_SOURCES} ${RESOURCES_SOURCES})
target_link_libraries(Qocoa ${QT_LIBRARIES})

View File

@@ -16,7 +16,9 @@ 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.
I'm not personally working on this any more but will accept pull-requests.
[![Build Status](https://travis-ci.org/MikeMcQuaid/Qocoa.svg?branch=master)](https://travis-ci.org/MikeMcQuaid/Qocoa)
## 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).
@@ -28,7 +30,9 @@ For each class you want to use copy the [`qocoa_mac.h`](https://github.com/mikem
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).
The 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/).
Magnifier and EditClear icons taken from [QtCreator](http://qt-project.org/) and are licensed under the [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)

View File

@@ -3,7 +3,6 @@
#include <QWidget>
#include <QPointer>
#include <QString>
class QButtonPrivate;
class QButton : public QWidget
@@ -24,7 +23,7 @@ public:
RoundRect = 12,
Recessed = 13,
RoundedDisclosure = 14,
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
Inline = 15
#endif
};

View File

@@ -37,7 +37,9 @@ public:
switch(bezelStyle) {
case QButton::Disclosure:
case QButton::Circular:
#ifdef __MAC_10_7
case QButton::Inline:
#endif
case QButton::RoundedDisclosure:
case QButton::HelpButton:
[nsButton setTitle:@""];
@@ -55,7 +57,7 @@ public:
font = [NSFont fontWithName:@"Lucida Grande Bold" size:12];
break;
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
case QButton::Inline:
font = [NSFont boldSystemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
break;
@@ -112,7 +114,7 @@ public:
qButton->setFixedHeight(22);
qButton->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
break;
#ifdef MAC_OS_X_VERSION_10_7
#ifdef __MAC_10_7
case QButton::Inline:
qButton->setMinimumWidth(10);
qButton->setFixedHeight(16);
@@ -130,7 +132,7 @@ public:
[nsButton setButtonType:NSMomentaryPushInButton];
}
[nsButton setBezelStyle:bezelStyle];
// [nsButton setBezelStyle:bezelStyle];
}
void clicked()

View File

@@ -20,19 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QPointer>
#include <QString>
#include <QPixmap>
#include <QAbstractButton>
#include <QBoxLayout>
#include <QPushButton>
#include "qbutton.h"
#include <QToolBar>
#include <QToolButton>
#include "qbutton.h"
#include <QPushButton>
#include <QVBoxLayout>
class QButtonPrivate : public QObject
{

View File

@@ -20,10 +20,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <AppKit/NSImage.h>
#include <Foundation/NSString.h>
#include <QByteArray>
#include <QString>
#include <QBoxLayout>
#include <QtMacExtras>
#include <QMacCocoaViewContainer>
static inline NSString* fromQString(const QString &string)
@@ -42,11 +43,11 @@ static inline QString toQString(NSString *string)
static inline NSImage* fromQPixmap(const QPixmap &pixmap)
{
CGImageRef cgImage = pixmap.toMacCGImageRef();
CGImageRef cgImage = QtMac::toCGImageRef(pixmap);
return [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize];
}
static inline void setupLayout(void *cocoaView, QWidget *parent)
static inline void setupLayout(NSView *cocoaView, QWidget *parent)
{
parent->setAttribute(Qt::WA_NativeWindow);
QVBoxLayout *layout = new QVBoxLayout(parent);

View File

@@ -17,7 +17,8 @@ public:
Aqua = 12
};
explicit QProgressIndicatorSpinning(QWidget *parent, Thickness thickness = Default);
explicit QProgressIndicatorSpinning(QWidget *parent,
Thickness thickness = Default);
public slots:
void animate(bool animate = true);
private:

View File

@@ -20,17 +20,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QBoxLayout>
#include <QLabel>
#include <QMovie>
#include <QPointer>
#include <QSize>
#include "qprogressindicatorspinning.h"
#include <QVBoxLayout>
#include <QMovie>
#include <QLabel>
class QProgressIndicatorSpinningPrivate : public QObject
{
public:

View File

@@ -2,22 +2,24 @@
#define QSEARCHFIELD_H
#include <QWidget>
#include <QObject>
#include <QPointer>
#include <QString>
#include <QEvent>
#include <QResizeEvent>
#include <QMenu>
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 reason);
void setFocus(Qt::FocusReason);
void setMenu(QMenu *menu);
public slots:
void setText(const QString &text);
@@ -31,15 +33,16 @@ signals:
void editingFinished();
void returnPressed();
private slots:
void popupMenu();
protected:
void changeEvent(QEvent*);
void resizeEvent(QResizeEvent*);
bool eventFilter(QObject*, QEvent*);
private:
friend class QSearchFieldPrivate;
QPointer <QSearchFieldPrivate> pimpl;
Q_PROPERTY(QString placeholderText READ placeholderText WRITE setPlaceholderText);
};
#endif // QSEARCHFIELD_H

View File

@@ -29,8 +29,8 @@ THE SOFTWARE.
#import "AppKit/NSSearchField.h"
#include <QApplication>
#include <QClipboard>
#include <QKeyEvent>
#include <QClipboard>
class QSearchFieldPrivate : public QObject
{
@@ -95,6 +95,16 @@ public:
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();
}
-(BOOL)control: (NSControl *)control textView:
(NSTextView *)textView doCommandBySelector:
(SEL)commandSelector {
@@ -111,16 +121,6 @@ public:
return NO;
}
-(void)controlTextDidEndEditing:(NSNotification*)notification {
// No Q_ASSERT here as it is called on destruction.
if (!pimpl) return;
pimpl->textDidEndEditing();
if ([[[notification userInfo] objectForKey:@"NSTextMovement"] intValue] == NSReturnTextMovement)
pimpl->returnPressed();
}
@end
@interface QocoaSearchField : NSSearchField
@@ -129,14 +129,13 @@ public:
@implementation QocoaSearchField
-(BOOL)performKeyEquivalent:(NSEvent*)event {
// First, check if we have the focus.
// If no, it probably means this event isn't for us.
NSResponder* firstResponder = [[NSApp keyWindow] firstResponder];
if ([firstResponder isKindOfClass:[NSText class]] &&
[(NSText*)firstResponder delegate] == self) {
if ([event type] == NSKeyDown && [event modifierFlags] & NSCommandKeyMask)
if ([event type] == NSEventTypeKeyDown && [event modifierFlags] & NSEventModifierFlagCommand)
{
QString keyString = toQString([event characters]);
if (keyString == "a") // Cmd+a
@@ -186,6 +185,25 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
[pool drain];
}
void QSearchField::setMenu(QMenu *menu)
{
Q_ASSERT(pimpl);
if (!pimpl)
return;
#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
NSMenu *nsMenu = menu->macMenu();
#else
NSMenu *nsMenu = menu->toNSMenu();
#endif
[[pimpl->nsSearchField cell] setSearchMenuTemplate:nsMenu];
}
void QSearchField::popupMenu()
{
}
void QSearchField::setText(const QString &text)
{
Q_ASSERT(pimpl);
@@ -194,10 +212,6 @@ void QSearchField::setText(const QString &text)
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[pimpl->nsSearchField setStringValue:fromQString(text)];
if (!text.isEmpty()) {
[pimpl->nsSearchField selectText:pimpl->nsSearchField];
[[pimpl->nsSearchField currentEditor] setSelectedRange:NSMakeRange([[pimpl->nsSearchField stringValue] length], 0)];
}
[pool drain];
}
@@ -212,40 +226,6 @@ void QSearchField::setPlaceholderText(const QString &text)
[pool drain];
}
QString QSearchField::placeholderText() const {
Q_ASSERT(pimpl);
NSString* placeholder = [[pimpl->nsSearchField cell] placeholderString];
return toQString(placeholder);
}
void QSearchField::setFocus(Qt::FocusReason reason)
{
/* Do nothing: we were previously using makeFirstResponder on search field, but
* that resulted in having the text being selected (and I didn't find any way to
* deselect it) which would result in the user erasing the first letter he just
* typed, after using setText (e.g. if the user typed a letter while having
* focus on the playlist, which means we call setText and give focus to the
* search bar).
* Instead now the focus will take place when calling selectText in setText.
* This obviously breaks the purpose of this function, but we never call only
* setFocus on a search box in Clementine (i.e. without a call to setText
* shortly after).
*/
// Q_ASSERT(pimpl);
// if (!pimpl)
// return;
// if ([pimpl->nsSearchField acceptsFirstResponder]) {
// [[pimpl->nsSearchField window] makeFirstResponder: pimpl->nsSearchField];
// }
}
void QSearchField::setFocus()
{
setFocus(Qt::OtherFocusReason);
}
void QSearchField::clear()
{
Q_ASSERT(pimpl);
@@ -274,12 +254,44 @@ QString QSearchField::text() const
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);
}
bool QSearchField::eventFilter(QObject *o, QEvent *e)
{
return QWidget::eventFilter(o, e);
}

View File

@@ -20,39 +20,48 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include <QtGlobal>
#include <QObject>
#include <QWidget>
#include <QApplication>
#include <QPointer>
#include <QString>
#include <QIcon>
#include <QSize>
#include <QStyle>
#include <QLineEdit>
#include <QBoxLayout>
#include <QToolButton>
#include <QtEvents>
#include "../../src/core/iconloader.h"
#include "qsearchfield.h"
#include <QLineEdit>
#include <QVBoxLayout>
#include <QToolButton>
#include <QStyle>
#include <QApplication>
#include <QDesktopWidget>
#include <QDir>
#include <QDebug>
class QSearchFieldPrivate : public QObject
{
public:
QSearchFieldPrivate(QSearchField *searchField, QLineEdit *lineEdit, QToolButton *clearButton)
: QObject(searchField), lineEdit(lineEdit), clearButton(clearButton) {}
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<QLineEdit> lineEdit;
QPointer<QToolButton> clearButton;
QPointer<QToolButton> searchButton;
QPointer<QMenu> searchMenu;
};
QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
@@ -67,29 +76,94 @@ QSearchField::QSearchField(QWidget *parent) : QWidget(parent)
connect(lineEdit, SIGNAL(textChanged(QString)),
this, SLOT(setText(QString)));
QIcon clearIcon(IconLoader::Load("edit-clear-locationbar-ltr"));
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(16, 16));
clearButton->setStyleSheet("border: none; padding: 0px;");
clearButton->resize(clearButton->sizeHint());
clearButton->setIconSize(QSize(iconsize, iconsize));
clearButton->setFixedSize(QSize(iconsize, iconsize));
clearButton->setStyleSheet("border: none;");
clearButton->hide();
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton);
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()));
const int frame_width = lineEdit->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
pimpl = new QSearchFieldPrivate(this, lineEdit, clearButton, searchButton);
lineEdit->setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(clearButton->width()));
const int width = frame_width + qMax(lineEdit->minimumSizeHint().width(), pimpl->clearButtonPaddedWidth());
const int height = frame_width + qMax(lineEdit->minimumSizeHint().height(), pimpl->clearButtonPaddedHeight());
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);
}
lineEdit->installEventFilter(this);
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)
@@ -98,6 +172,8 @@ void QSearchField::setText(const QString &text)
if (!(pimpl && pimpl->clearButton && pimpl->lineEdit))
return;
pimpl->clearButton->setVisible(!text.isEmpty());
if (text != this->text())
pimpl->lineEdit->setText(text);
}
@@ -113,26 +189,6 @@ void QSearchField::setPlaceholderText(const QString &text)
#endif
}
QString QSearchField::placeholderText() const {
#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::clear()
{
Q_ASSERT(pimpl && pimpl->lineEdit);
@@ -160,6 +216,30 @@ QString QSearchField::text() const
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);
@@ -167,22 +247,10 @@ void QSearchField::resizeEvent(QResizeEvent *resizeEvent)
return;
QWidget::resizeEvent(resizeEvent);
const int x = pimpl->lineEditFrameWidth();
const int x = width() - pimpl->clearButtonPaddedWidth();
const int y = (height() - pimpl->clearButton->height())/2;
pimpl->clearButton->move(x, y);
}
bool QSearchField::eventFilter(QObject *o, QEvent *e)
{
if (pimpl && pimpl->lineEdit && o == pimpl->lineEdit) {
// Forward some lineEdit events to QSearchField (only those we need for
// now, but some might be added later if needed)
switch (e->type()) {
case QEvent::FocusIn:
case QEvent::FocusOut:
QApplication::sendEvent(this, e);
break;
}
}
return QWidget::eventFilter(o, e);
pimpl->searchButton->move(pimpl->lineEditFrameWidth() * 2,
(height() - pimpl->searchButton->height())/2);
}