Implementing pinned menu items
I'm trying to implement pinned menu items for use in a list of recently opened files. (For example, like Microsoft Office 2007 and 2010 do.) I'm almost there, but I haven't been able to properly align the pin images. What I need is something like putting a grid layout on the whole menu, but I don't think I can do that. Any suggestions? I've appended my pinmenu code and a test program.
Code:
// pinmenu.cpp
#include "pinmenu.h"
PinMenu
::PinMenu(const QString
& title,
int id,
bool pinstate
) : QWidgetAction(NULL) {
pinLayout->setMargin(0);
pinLayout->setContentsMargins(5, 3, 5, 3);
pinTitle = new ClickableLabel(title);
// This ugly attempt to line up the pin icons will break if title is too long,
// and it doesn't even succeed in lining up the icons very well.
int pixelWidth = pinTitle->fontMetrics().boundingRect(pinTitle->text()).width();
pinLayout->setSpacing(200 - pixelWidth);
pinLayout->addWidget(pinTitle);
pinImage = new ClickableLabel();
pinLayout->addWidget(pinImage);
pinWidget->setLayout(pinLayout);
setPinState(pinstate);
setDefaultWidget(pinWidget);
idno = id;
connect(pinTitle, SIGNAL(clicked()), this, SLOT(slot_textClicked()));
connect(pinImage, SIGNAL(clicked()), this, SLOT(slot_pinClicked()));
}
PinMenu::~PinMenu()
{
delete pinLayout;
delete pinWidget;
}
bool PinMenu::isPinned()
{
return pinned;
}
bool PinMenu::togglePin()
{
setPinState(!pinned);
return pinned;
}
void PinMenu::setPinState(bool pin)
{
pinned = pin;
pinImage->setPixmap(pinned ? pinmap : unpinmap);
}
void PinMenu::slot_textClicked()
{
emit signal_textClicked(idno);
}
void PinMenu::slot_pinClicked()
{
togglePin();
// Don't emit this signal - it closes the menu
// emit signal_pinClicked(idno);
}
Code:
// pinmenu.h
#ifndef PINMENU_H
#define PINMENU_H
#include <QWidgetAction>
#include <QHBoxLayout>
#include "clickablelabel.h"
{
Q_OBJECT
public:
PinMenu(const QString& title, int id, bool pinstate);
~PinMenu();
bool isPinned();
bool togglePin();
void setPinState(bool pin);
private:
int idno;
bool pinned;
ClickableLabel *pinImage;
ClickableLabel *pinTitle;
private slots:
void slot_textClicked();
void slot_pinClicked();
signals:
void signal_textClicked(int);
void signal_pinClicked(int);
};
#endif // PINMENU_H
Code:
// main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
MainWindow w(0);
w.show();
return a.exec();
}
Code:
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "pinmenu.h"
#include <QMessageBox>
#include <QHBoxLayout>
#include <QLabel>
MainWindow
::MainWindow(QWidget *parent
) {
ui->setupUi(this);
QMenu *fileMenu
= menuBar
()->addMenu
(tr
("&File"));
// Avoid warning that open is not used in this test program
// QAction *open = addMenuItem(fileMenu, tr("&Open"), SLOT(slot_open_clicked()));
addMenuItem(fileMenu, tr("&Open"), SLOT(slot_open_clicked()));
QMenu *openRecentFilesMenu
= fileMenu
->addMenu
(tr
("&Recently used files"));
addMenuItem(fileMenu, tr("E&xit"), SLOT(slot_close_clicked()));
PinMenu *pinMenuAction1 = new PinMenu(tr("Short name"), 1, true);
openRecentFilesMenu->addAction(pinMenuAction1);
connect(pinMenuAction1, SIGNAL(signal_textClicked(int)), this, SLOT(slot_textClicked(int)));
// connect(pinMenuAction1, SIGNAL(signal_pinClicked(int)), this, SLOT(slot_iconClicked(int)));
PinMenu *pinMenuAction2 = new PinMenu(tr("Long name to test alignment"), 2, false);
openRecentFilesMenu->addAction(pinMenuAction2);
connect(pinMenuAction2, SIGNAL(signal_textClicked(int)), this, SLOT(slot_textClicked(int)));
// connect(pinMenuAction2, SIGNAL(signal_pinClicked(int)), this, SLOT(slot_iconClicked(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::slot_close_clicked()
{
close();
}
void MainWindow::slot_open_clicked()
{
// Code here to open file
}
void MainWindow::slot_textClicked(int id)
{
QMessageBox::information(this,
"Info", tr
("Text clicked, id = %1").
arg(id
));
}
void MainWindow::slot_iconClicked(int id)
{
QMessageBox::information(this,
"Info", tr
("Icon clicked, id = %1").
arg(id
));
}
{
connect(openAction, SIGNAL(triggered()), this, method);
whichMenu->addAction(openAction);
return openAction;
}
Code:
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui/QMainWindow>
namespace Ui
{
class MainWindow;
}
{
Q_OBJECT
public:
~MainWindow();
private:
private slots:
void slot_close_clicked();
void slot_open_clicked();
void slot_iconClicked(int id);
void slot_textClicked(int id);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
Code:
// clickablelabel.cpp
#include "clickablelabel.h"
// This comes from http://qt-project.org/wiki/Make-a-QLabel-Clickable
// License is Creative Commons Attribution-ShareAlike 2.5 Generic
ClickableLabel
::ClickableLabel(const QString
& text,
QWidget *parent
) : {
this->setText(text);
}
ClickableLabel::~ClickableLabel()
{
}
void ClickableLabel
::mousePressEvent (QMouseEvent * /* event */) {
emit clicked();
}
Code:
// clickablelabel.h
#ifndef CLICKABLELABEL_H
#define CLICKABLELABEL_H
// This comes from http://qt-project.org/wiki/Make-a-QLabel-Clickable
// License is Creative Commons Attribution-ShareAlike 2.5 Generic
#include <QLabel>
class ClickableLabel
: public QLabel {
Q_OBJECT
public:
explicit ClickableLabel
(const QString
& text
="",
QWidget *parent
= 0);
~ClickableLabel();
signals:
void clicked();
protected:
};
#endif // CLICKABLELABEL_H
Re: Implementing pinned menu items
I found a solution that works although it seems ugly. Generate the menu without worrying about aligning the pin icons, then go back and fix the alignment by adjusting the spacing.
In more detail:
set the spacing of every entry to some value, say 20 pixels
call menu->ensurePolished
find the menu item with the largest width
increase the spacing of every other menu item to bring them up to this width.