Qt4 Mac SearchBox Wrapper
August 17, 2008 Matteo Bertozzi | Filed Under Mac OS X, Qt4 | No CommentsDo you want to use Mac OS X SearchBox in your Qt Application? Ok, pick up your XCode or your preferred code editor and we’ll start to write a Qt Wrapper for Carbon HISearchField.
The Header File will be something like this, with two signals textChanged(const QString&) that will be used to “Filter on the Fly” it will be raised when the user type something, findNext() will be raised when user press Return Button.
#if defined(Q_WS_MAC) && !defined(_QF_MAC_SEARCHBOX_H_)
#define _QF_MAC_SEARCHBOX_H_
// Qt4 Headers
#include <QWidget>
// Mac OS X - Carbon Headers
#include <Carbon/Carbon.h>
class QfMacSearchBox : public QWidget {
Q_OBJECT
public:
QfMacSearchBox (QWidget *parent = 0);
~QfMacSearchBox();
// Methods
void raiseTextChanged (void);
void raiseFindNext (void);
QSize sizeHint (void) const;
// GET Properties
QString text (void) const;
signals:
void textChanged (const QString& text);
void findNext (void);
public slots:
void setText (const QString& text);
void clear (void);
private:
CFStringRef searchFieldText;
HIViewRef searchField;
};
#endif // Q_WS_MAC && !_QF_MAC_SEARCHBOX_H_
And now the Class Implementation. I’ve internally managed the clear button, so you haven’t a clear signals but a textChanged(const QString&) signal with an empty string.
// Qt4 Headers
#include <QVarLengthArray>
#include <QMenu>
// SearchBox Headers
#include "searchbox_mac.h"
// =======================================
// MacSearchBox: PRIVATE FILE Methods
// =======================================
static QString toQString(CFStringRef str) {
if(!str)
return QString();
CFIndex length = CFStringGetLength(str);
const UniChar *chars = CFStringGetCharactersPtr(str);
if (chars)
return QString(reinterpret_cast<const QChar *>(chars), length);
QVarLengthArray<UniChar> buffer(length);
CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data());
return QString(reinterpret_cast<const QChar *>(buffer.constData()), length);
}
static OSStatus SearchFieldEventHandler(EventHandlerCallRef handlerCallRef,
EventRef event, void *userData)
{
QfMacSearchBox *searchBox = (QfMacSearchBox *) userData;
OSType eventClass = GetEventClass(event);
UInt32 eventKind = GetEventKind(event);
if (eventClass == kEventClassSearchField) {
switch (eventKind) {
case kEventSearchFieldCancelClicked:
searchBox->clear();
break;
case kEventSearchFieldSearchClicked:
searchBox->raiseFindNext();
break;
default:
break;
}
} else if (eventClass == kEventClassTextField) {
switch (eventKind) {
case kEventTextDidChange:
searchBox->raiseTextChanged();
break;
case kEventTextAccepted:
searchBox->raiseFindNext();
break;
default:
break;
}
}
return(eventNotHandledErr);
}
// =======================================
// MacSearchBox: PUBLIC Constructors/Destructors
// =======================================
QfMacSearchBox::QfMacSearchBox (QWidget *parent)
: QWidget(parent)
{
// Set Widget Properties
//setFocusPolicy(Qt::StrongFocus);
setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
// Create a native search field and pass its window id to QWidget::create.
searchFieldText = CFStringCreateWithCString(0,
(const char *) tr("Search").toAscii(), 0);
HISearchFieldCreate(NULL, kHISearchFieldAttributesSearchIcon |
kHISearchFieldAttributesCancel,
NULL, searchFieldText, &searchField);
create(reinterpret_cast<WId>(searchField));
// Subscribe Events
EventTypeSpec mySFieldEvents[] = {
{ kEventClassSearchField, kEventSearchFieldCancelClicked },
{ kEventClassTextField, kEventTextDidChange },
{ kEventClassTextField, kEventTextAccepted }
};
HIViewInstallEventHandler(searchField, SearchFieldEventHandler,
GetEventTypeCount(mySFieldEvents), mySFieldEvents,
(void *) this, NULL);
// Use a Qt menu for the search field menu.
QMenu *searchMenu = new QMenu(this);
QAction *indexAction = searchMenu->addAction(tr("Index Search"));
indexAction->setCheckable(true);
indexAction->setChecked(true);
QAction *fulltextAction = searchMenu->addAction(tr("Full Text Search"));
fulltextAction->setCheckable(true);
QActionGroup *searchActionGroup = new QActionGroup(this);
searchActionGroup->addAction(indexAction);
searchActionGroup->addAction(fulltextAction);
searchActionGroup->setExclusive(true);
MenuRef macSearchMenu = searchMenu->macMenu(0);
HISearchFieldSetSearchMenu(searchField, macSearchMenu);
}
QfMacSearchBox::~QfMacSearchBox() {
CFRelease(searchField);
CFRelease(searchFieldText);
}
// =======================================
// MacSearchBox: PUBLIC Methods
// =======================================
void QfMacSearchBox::raiseTextChanged (void) {
emit textChanged(text());
}
void QfMacSearchBox::raiseFindNext (void) {
emit findNext();
}
QSize QfMacSearchBox::sizeHint (void) const {
HIRect optimalBounds;
EventRef event;
CreateEvent(0, kEventClassControl, kEventControlGetOptimalBounds,
GetCurrentEventTime(), kEventAttributeUserEvent, &event);
SendEventToEventTargetWithOptions(event,
HIObjectGetEventTarget(HIObjectRef(winId())),
kEventTargetDontPropagate);
GetEventParameter(event, kEventParamControlOptimalBounds, typeHIRect,
0, sizeof(HIRect), 0, &optimalBounds);
ReleaseEvent(event);
return QSize(optimalBounds.size.width + 200, optimalBounds.size.height);
}
// =======================================
// MacSearchBox: PUBLIC GET Properties
// =======================================
QString QfMacSearchBox::text (void) const {
CFStringRef cfString = HIViewCopyText(searchField);
QString text = toQString(cfString);
CFRelease(cfString);
return(text);
}
// =======================================
// MacSearchBox: PUBLIC SET Properties
// =======================================
void QfMacSearchBox::setText (const QString& text) {
CFRelease(searchFieldText);
searchFieldText = CFStringCreateWithCString(0,
(const char *) text.toAscii(), 0);
HIViewSetText(searchField, searchFieldText);
emit textChanged(text);
}
void QfMacSearchBox::clear (void) {
setText(QString());
}
That’s all folks.
