Merge lp:~christian-w/lightdm/qt-binding-keyboard-layouts into lp:lightdm

Proposed by Christian Seiler
Status: Needs review
Proposed branch: lp:~christian-w/lightdm/qt-binding-keyboard-layouts
Merge into: lp:lightdm
Diff against target: 249 lines (+213/-0)
4 files modified
liblightdm-qt/Makefile.am (+3/-0)
liblightdm-qt/QLightDM/LayoutsModel (+1/-0)
liblightdm-qt/QLightDM/layoutsmodel.h (+60/-0)
liblightdm-qt/layoutsmodel.cpp (+149/-0)
To merge this branch: bzr merge lp:~christian-w/lightdm/qt-binding-keyboard-layouts
Reviewer Review Type Date Requested Status
Michael Terry Approve
Review via email: mp+205356@code.launchpad.net

Commit message

Add support for keyboard layouts to Qt bindings

Propagate keyboard layout API to Qt bindings to make it possible to
write Qt greeters that offer layout selection.

Description of the change

The API is not 100% optimal when it comes to actually choosing the
layout itself (shouldn't be part of the model, but some extra
functions), but being able to call them from QML is otherwise not so
trivial.

To post a comment you must log in.
Revision history for this message
David Edmundson (david.edmundson) wrote :

37 + * Copyright (C) 2010-2011 David Edmundson.
38 + * Author: David Edmundson <email address hidden>

Either I was very drunk when writing this and forgot, or you've clearly copy and pasted this :)
Remember to set 2014 too.

------

+void LayoutsModel::setCurrentLayout(QVariant layoutData)

const & layoutData. Save yourself a copy.

--

I would register LightDMLayout as a metatype
Q_DECLARE_METATYPE(LightDMLayout)

and then you shouldn't need to cast to void*.

Right now if I did
myLayoutModel.layout = someRandomQObject from inside the QML it will crash.

QVariant will be able to cast it to void* and static_cast<> almost always succeeds.
We don't want to let the greeters be able to cause a crash.

--

As for the API. I know what you mean, it's a bit sucky but I would agree with you that this is one of the least terrible ways to do it.

I'd change it to a property. Otherwise you need to have a component.onCompleted which is messy; you have the data when you load, you may as well bind to it initially.

The other approach I've seen is to have a property with the current index. This has the disadvantage of getting confusing if the model becomes dynamicly changing; but has the awesome advantage that you can select the right index in a combo box etc without having to have an awkward loop in javascript.

--
LayoutsModel::data

FYI if you want to pass against QtModelTest (http://qt-project.org/wiki/Model_Test) you'll need to check bounds here i.e row > 0, row < rowCount(), column == 0 etc and return a QVariant otherwise.
I doubt I do it anywhere else, so I don't really care about this one.

Revision history for this message
Christian Seiler (christian-w) wrote :

Thanks for reviewing!

> 37 + * Copyright (C) 2010-2011 David Edmundson.
> 38 + * Author: David Edmundson <email address hidden>
>
> Either I was very drunk when writing this and forgot, or you've clearly copy and pasted this :)
> Remember to set 2014 too.

Well, most of the file was mainly C&P, I don't claim that my change is
terribly deep. ;-) But I'll fix that.

> ------
>
> +void LayoutsModel::setCurrentLayout(QVariant layoutData)
>
> const & layoutData. Save yourself a copy.

Ok.

> I would register LightDMLayout as a metatype
> Q_DECLARE_METATYPE(LightDMLayout)
>
> and then you shouldn't need to cast to void*.

You mean LightDMLayout*, right? And I could probably do that just
internally? Because I don't want to leak Glib types into the Qt API...

(Err, scratch that, see below...)

> Right now if I did
> myLayoutModel.layout = someRandomQObject from inside the QML it will crash.
>
> QVariant will be able to cast it to void* and static_cast<> almost always succeeds.

Oh, I didn't know that... Ooops.

> We don't want to let the greeters be able to cause a crash.

At least the QML ones; C++ can cause crashes left and right
regardless. ;-)

> The other approach I've seen is to have a property with the current
> index. This has the disadvantage of getting confusing if the model
> becomes dynamicly changing; but has the awesome advantage that you can
> select the right index in a combo box etc without having to have an
> awkward loop in javascript.

Oh, that sounds cool, I'll do that then. Since at least the Glib API
currently doesn't provide any possibility for dynamical changes, I don't
think this is going to be a problem, we just assume everything is
static. Once that changes, we could always revisit.

I also had a minor epiphany for the naming convention, how about
'activeLayoutIndex' for the property name?

And then I also don't need to wrap LightDMLayout in a QVariant, since it
will just be a model index -> win/win. :)

> LayoutsModel::data
>
> FYI if you want to pass against QtModelTest (http://qt-project.org/wiki/Model_Test) you'll need to check bounds here i.e row > 0, row < rowCount(), column == 0 etc and return a QVariant otherwise.
> I doubt I do it anywhere else, so I don't really care about this one.

I just C&P'd the other code. ;-) But I don't object to adding additional
safety checks just in case to this part, I'm just not going to touch the
rest.

Regards,
Christian

PS: Since I'm new to launchpad, what is the etiquette for updating merge
requests? Just push additional commits on top of the previous one to the
branch?

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Christian - yes, just push new commits. David, any other opposition to this? I'm just waiting on your approval.

Revision history for this message
Christian Seiler (christian-w) wrote :

Hi,

> Christian - yes, just push new commits. David, any other opposition
> to this? I'm just waiting on your approval.

I was actually waiting for a comment on my suggestion w.r.t. the layouts
API that I made here, before I then update the Qt bindings:
http://lists.freedesktop.org/archives/lightdm/2014-February/000509.html

Since I don't think anybody is using the current API yet, this would be
the ideal place to redesign them a bit to make them more useful.

If you agree to changing the API, I would provide a new merge request
for the modified Glib API that also includes Qt bindings for those.

Regards,
Christian

Revision history for this message
Robert Ancell (robert-ancell) wrote :

David?

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Christian - I think it would be worth fixing this and pushing it in as-is. We can update the API at a later date

Revision history for this message
Michael Terry (mterry) wrote :

Looks fine to me. I didn't run/test the code, but looks OK. I'm also fine with updating the layouts API, but seems like we can do that later, agreed.

review: Approve
Revision history for this message
David Edmundson (david.edmundson) wrote :

I think it would be a good idea for you to address my first two review comments.
Ignore the bit after "The other approach", the bit beforehand about casting will prevent a potential awkward crash in the future.

But feel free to approve it if you think otherwise.

Revision history for this message
Christian Seiler (christian-w) wrote :

> I think it would be a good idea for you to address my first two review
> comments.

I didn't do that because I was waiting for a response to my suggestion
about rewriting the API.

But if you feel you first want to have the bindings for the current API
in before discussing API changes, I'll update my merge request for this
tomorrow.

Revision history for this message
David Edmundson (david.edmundson) wrote :

Christian, sorry about that.
I lost track of things and forgot.

Revision history for this message
Robert Ancell (robert-ancell) wrote :

Let's merge this as is. Please just update the copyright header.

Unmerged revisions

1886. By Christian Seiler

Add support for keyboard layouts to Qt bindings

Propagate keyboard layout API to Qt bindings to make it possible to
write Qt greeters that offer layout selection.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'liblightdm-qt/Makefile.am'
--- liblightdm-qt/Makefile.am 2014-02-06 15:35:02 +0000
+++ liblightdm-qt/Makefile.am 2014-02-07 12:39:23 +0000
@@ -25,10 +25,12 @@
2525
26common_headers = \26common_headers = \
27 QLightDM/Greeter \27 QLightDM/Greeter \
28 QLightDM/LayoutsModel \
28 QLightDM/Power \29 QLightDM/Power \
29 QLightDM/SessionsModel \30 QLightDM/SessionsModel \
30 QLightDM/UsersModel \31 QLightDM/UsersModel \
31 QLightDM/greeter.h \32 QLightDM/greeter.h \
33 QLightDM/layoutsmodel.h \
32 QLightDM/power.h \34 QLightDM/power.h \
33 QLightDM/sessionsmodel.h \35 QLightDM/sessionsmodel.h \
34 QLightDM/usersmodel.h36 QLightDM/usersmodel.h
@@ -38,6 +40,7 @@
3840
39common_sources = \41common_sources = \
40 greeter.cpp \42 greeter.cpp \
43 layoutsmodel.cpp \
41 power.cpp \44 power.cpp \
42 sessionsmodel.cpp \45 sessionsmodel.cpp \
43 usersmodel.cpp46 usersmodel.cpp
4447
=== added file 'liblightdm-qt/QLightDM/LayoutsModel'
--- liblightdm-qt/QLightDM/LayoutsModel 1970-01-01 00:00:00 +0000
+++ liblightdm-qt/QLightDM/LayoutsModel 2014-02-07 12:39:23 +0000
@@ -0,0 +1,1 @@
1#include "QLightDM/layoutsmodel.h"
02
=== added file 'liblightdm-qt/QLightDM/layoutsmodel.h'
--- liblightdm-qt/QLightDM/layoutsmodel.h 1970-01-01 00:00:00 +0000
+++ liblightdm-qt/QLightDM/layoutsmodel.h 2014-02-07 12:39:23 +0000
@@ -0,0 +1,60 @@
1/*
2 * Copyright (C) 2010-2011 David Edmundson.
3 * Author: David Edmundson <kde@davidedmundson.co.uk>
4 *
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; either version 2 or version 3 of the License.
8 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
9 */
10
11#ifndef QLIGHTDM_LAYOUTS_MODEL_H
12#define QLIGHTDM_LAYOUTS_MODEL_H
13
14#include <QtCore/QAbstractListModel>
15
16namespace QLightDM {
17class LayoutsModelPrivate;
18
19class Q_DECL_EXPORT LayoutsModel : public QAbstractListModel
20{
21 Q_OBJECT
22
23 Q_ENUMS(LayoutModelRoles)
24
25public:
26 enum LayoutModelRoles {
27 DescriptionRole = Qt::DisplayRole,
28 NameRole = Qt::UserRole,
29 ShortDescriptionRole,
30 InternalDataRole,
31 };
32
33 explicit LayoutsModel(QObject *parent = 0);
34 virtual ~LayoutsModel();
35
36 int rowCount(const QModelIndex &parent) const;
37 QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
38
39 /* These do not technically belong into the model, and could
40 * in principle be static, but we want to be able to use this
41 * stuff from QML, so we just add them as methods here. It's
42 * not optimal, but better than nothing.
43 *
44 * The QVariant is the same as the item's InternalDataRole and
45 * should be considered opaque. Just pass the InternalDataRole
46 * data of an item to setKeyboardLayout to change the current
47 * keyboard layout. */
48 Q_INVOKABLE QVariant getCurrentLayout() const;
49 Q_INVOKABLE void setCurrentLayout(QVariant layoutData);
50
51protected:
52 LayoutsModelPrivate *d_ptr;
53
54private:
55 Q_DECLARE_PRIVATE(LayoutsModel)
56};
57
58}
59
60#endif // QLIGHTDM_LAYOUTS_MODEL_H
061
=== added file 'liblightdm-qt/layoutsmodel.cpp'
--- liblightdm-qt/layoutsmodel.cpp 1970-01-01 00:00:00 +0000
+++ liblightdm-qt/layoutsmodel.cpp 2014-02-07 12:39:23 +0000
@@ -0,0 +1,149 @@
1/*
2 * Copyright (C) 2010-2011 David Edmundson.
3 * Author: David Edmundson <kde@davidedmundson.co.uk>
4 *
5 * This library is free software; you can redistribute it and/or modify it under
6 * the terms of the GNU Lesser General Public License as published by the Free
7 * Software Foundation; either version 2 or version 3 of the License.
8 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
9 */
10
11#include "QLightDM/layoutsmodel.h"
12
13#include <QtCore/QString>
14#include <QtCore/QDebug>
15#include <QtGui/QIcon>
16
17#include <lightdm.h>
18
19using namespace QLightDM;
20
21class LayoutItem
22{
23public:
24 QString name;
25 QString shortDescription;
26 QString description;
27
28 LightDMLayout *ldmLayout;
29};
30
31namespace QLightDM {
32class LayoutsModelPrivate {
33public:
34 LayoutsModelPrivate(LayoutsModel *parent);
35 QList<LayoutItem> layouts;
36
37 protected:
38 LayoutsModel * const q_ptr;
39
40 void loadLayouts();
41 private:
42 Q_DECLARE_PUBLIC(LayoutsModel)
43};
44}
45
46LayoutsModelPrivate::LayoutsModelPrivate(LayoutsModel* parent) :
47 q_ptr(parent)
48{
49#if !defined(GLIB_VERSION_2_36)
50 g_type_init();
51#endif
52}
53
54void LayoutsModelPrivate::loadLayouts()
55{
56 GList *ldmLayouts;
57
58 ldmLayouts = lightdm_get_layouts();
59
60 for (GList* item = ldmLayouts; item; item = item->next) {
61 LightDMLayout *ldmLayout = static_cast<LightDMLayout*>(item->data);
62 Q_ASSERT(ldmLayout);
63
64 LayoutItem layout;
65 layout.name = QString::fromUtf8(lightdm_layout_get_name(ldmLayout));
66 layout.shortDescription = QString::fromUtf8(lightdm_layout_get_short_description(ldmLayout));
67 layout.description = QString::fromUtf8(lightdm_layout_get_description(ldmLayout));
68 layout.ldmLayout = ldmLayout;
69
70 layouts.append(layout);
71 }
72}
73
74LayoutsModel::LayoutsModel(QObject *parent) :
75 QAbstractListModel(parent),
76 d_ptr(new LayoutsModelPrivate(this))
77{
78 Q_D(LayoutsModel);
79 // Extend roleNames (we want to keep the "display" role)
80 QHash<int, QByteArray> roles = roleNames();
81 roles[NameRole] = "name";
82 roles[ShortDescriptionRole] = "shortDescription";
83 roles[DescriptionRole] = "description";
84 roles[InternalDataRole] = "internalData";
85 setRoleNames(roles);
86 d->loadLayouts();
87}
88
89LayoutsModel::~LayoutsModel()
90{
91 delete d_ptr;
92}
93
94
95int LayoutsModel::rowCount(const QModelIndex &parent) const
96{
97 Q_D(const LayoutsModel);
98 if (parent == QModelIndex()) {
99 return d->layouts.size();
100 }
101
102 return 0;
103}
104
105QVariant LayoutsModel::data(const QModelIndex &index, int role) const
106{
107 Q_D(const LayoutsModel);
108
109 if (!index.isValid()) {
110 return QVariant();
111 }
112
113 int row = index.row();
114 switch (role) {
115 case LayoutsModel::NameRole:
116 return d->layouts[row].name;
117 case LayoutsModel::ShortDescriptionRole:
118 return d->layouts[row].shortDescription;
119 case LayoutsModel::DescriptionRole:
120 return d->layouts[row].description;
121 case LayoutsModel::InternalDataRole:
122 return qVariantFromValue(static_cast<void *>(d->layouts[row].ldmLayout));
123 }
124
125 return QVariant();
126}
127
128QVariant LayoutsModel::getCurrentLayout() const
129{
130 LightDMLayout *layout = lightdm_get_layout();
131 if (layout)
132 return qVariantFromValue(static_cast<void *>(layout));
133 return QVariant();
134}
135
136void LayoutsModel::setCurrentLayout(QVariant layoutData)
137{
138 if (layoutData.isValid() && layoutData.canConvert<void *>()) {
139 LightDMLayout *layout = static_cast<LightDMLayout *>(layoutData.value<void *>());
140 if (layout)
141 lightdm_set_layout(layout);
142 }
143}
144
145#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
146#include "layoutsmodel_moc5.cpp"
147#else
148#include "layoutsmodel_moc4.cpp"
149#endif

Subscribers

People subscribed via source and target branches