PDA

View Full Version : Implementing pinned menu items



wssddc
19th July 2013, 06:44
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.


// pinmenu.cpp
#include "pinmenu.h"

PinMenu::PinMenu(const QString& title, int id, bool pinstate) : QWidgetAction(NULL)
{
pinmap = QPixmap(":/pin.png");
unpinmap = QPixmap(":/unpin.png");
pinWidget = new QWidget();
pinLayout = new QHBoxLayout();
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);
}


// pinmenu.h
#ifndef PINMENU_H
#define PINMENU_H

#include <QWidgetAction>
#include <QHBoxLayout>
#include "clickablelabel.h"

class PinMenu : public QWidgetAction
{
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;
QPixmap pinmap;
QPixmap unpinmap;
QWidget *pinWidget;
QHBoxLayout *pinLayout;

private slots:
void slot_textClicked();
void slot_pinClicked();

signals:
void signal_textClicked(int);
void signal_pinClicked(int);
};

#endif // PINMENU_H


// main.cpp
#include <QtGui/QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w(0);
w.show();
return a.exec();
}


// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "pinmenu.h"

#include <QMessageBox>
#include <QHBoxLayout>
#include <QLabel>

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow)
{
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)));
fileMenu->setLayout(new QGridLayout());
}

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));
}

QAction *MainWindow::addMenuItem(QMenu *whichMenu, QString menuText, const char *method)
{
QAction *openAction = new QAction(menuText, this);
connect(openAction, SIGNAL(triggered()), this, method);
whichMenu->addAction(openAction);
return openAction;
}


// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>

namespace Ui
{
class MainWindow;
}

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = 0);
~MainWindow();

private:
QAction *addMenuItem(QMenu *whichMenu, QString menuText, const char *method);

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


// 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) :
QLabel(parent)
{
this->setText(text);
}

ClickableLabel::~ClickableLabel()
{
}

void ClickableLabel::mousePressEvent (QMouseEvent * /* event */)
{
emit clicked();
}


// 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:
void mousePressEvent(QMouseEvent *event) ;
};

#endif // CLICKABLELABEL_H

wssddc
30th July 2013, 05:16
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.