[Qt-components] Ubuntu UI toolkit - Clipboard access

Zsombor Egri zsombor.egri at canonical.com
Thu Jan 24 08:38:51 CET 2013


On 01/23/2013 09:09 PM, Alan Alpert wrote:
> On Wed, Jan 23, 2013 at 10:39 AM, Zsombor Egri
> <zsombor.egri at canonical.com> wrote:
>> On 01/23/2013 07:55 PM, Alan Alpert wrote:
>>> On Wed, Jan 23, 2013 at 9:00 AM, Zsombor Egri
>>> <zsombor.egri at canonical.com> wrote:
>>>> On 01/23/2013 05:49 PM, Alan Alpert wrote:
>>>>> On Wed, Jan 23, 2013 at 3:15 AM, Zsombor Egri
>>>>> <zsombor.egri at canonical.com> wrote:
>>>>>> On 01/23/2013 12:09 PM, Aaron J. Seigo wrote:
>>>>>>
>>>>>> On Wednesday, January 23, 2013 10:06:59 Zsombor Egri wrote:
>>>>>>
>>>>>> Here's an API we are proposing for accessing the system clipboard from
>>>>>> QML and to filter its content before being copied into a TextField/TextArea.
>>>>>>
>>>>>> proposing to be added to Qt's QML API? (that seems the best place for such a
>>>>>> thing)
>>>>>>
>>>>>> This was proposed for the Ubuntu UI toolkit, just wanted to share with you
>>>>>> because I see it makes sense to have such a component in Qt QML.
>>>>>>
>>>>>>
>>>>>> The API consists of an object, a data transfer type and a filter element.
>>>>>>
>>>>>> The global object is based on (and uses) QClipboard functionality:
>>>>>>
>>>>>> *clipboard* {
>>>>>>     function *clear*() - to clear the clipboard content
>>>>>>     function *setData*(string mimeType, var data) - pushes data to the
>>>>>> clipboard
>>>>>>     function MimeData *data*() - retrieves the clipboard data
>>>>>> }
>>>>>>
>>>>>> i'm not a fan of global objects in the least. much nicer to instantiate
>>>>>> items
>>>>>> that are actually needed. otherwise, we end up with a growing pile of
>>>>>> objects
>>>>>> of limited value in many use cases.
>>>>>>
>>>>>> Agree. The unicity of the Clipboard per application drove me to have the
>>>>>> object instead of instantiating items per need.
>>>>> Use the new Singleton Type API:
>>>>> http://qt-project.org/doc/qt-5.0/qtqml/qqmlengine.html#qmlRegisterSingletonType-2
>>>>>
>>>>> Only one instance per application, easier to use than a global object,
>>>>> and still not created until the application needs it.
>>>>>
>>>>> Also the setData/data and clear sure look like that should just be one
>>>>> data property with a setter, getter and reset function.
>>>>>
>>>>>> also, why functions instead of a data property? an object with mimeType and
>>>>>> data properties could be assigned/retrieved from it. setting an invalid
>>>>>> object
>>>>>> would be a no-op.
>>>>>>
>>>>>> it also makes clear unnecessary: just set data to be an empty / invalid
>>>>>> MimeData object.
>>>>>>
>>>>>> Makes sense.
>>>>>>
>>>>>>
>>>>>> The MimeData type provides access to the clipboard data itself; all
>>>>>> properties are read-only.
>>>>>>
>>>>>> *MimeData* {
>>>>>>     readonly property list<string> *types* - contains the available
>>>>>> types in the clipboard
>>>>>>     readonly property string *text* - text data or undefined if no text
>>>>>> data in clipboard
>>>>>>     readonly property string *html* - HTML data or undefined
>>>>>>     readonly property list<url> *urls* - list or URLs or undefined
>>>>>>     readonly property color *color* - color data or undefined
>>>>>>
>>>>>>     function var *mimeData*(string type) - returns data corresponding to
>>>>>> the MIME type
>>>>>> }
>>>>>>
>>>>>> why not a single variant property?
>>>>>> why a function to return the mimeData? this is going to be implemented in
>>>>>> C++
>>>>>> i imagine anyways, so a property that is retrieved via a C++ function seems
>>>>>> normal. it can be set to read-only.
>>>>> If we aren't exposing the full QMimeData functionality anyways, why
>>>>> not just add these as convenience functions on the global clipboard
>>>>> object?
>>>> I was about to send a new API proposal with one object when I got your
>>>> reply :)
>>>>>> A single variant property may make sense if the clipboard could have only
>>>>>> one mime type data inside. But you can have more of those at the same time,
>>>>>> so the properties would retrieve the particular data types when the mimeData
>>>>>> function would retrieve other types. A pair of (type, data) property would
>>>>>> also do the job, where setting the type would automatically alter the data,
>>>>>> but I find those kind of dependencies a bit cumbersome...
>>>>>>
>>>>>>
>>>>>> And finally the clipboard filter element, which is meant to capture
>>>>>> clipboard changes aswell as to grab clipboard key shortcuts from a
>>>>>> TextField or TextArea so data can be filtered before pasting it into the
>>>>>> text inputs.
>>>>> I'd also have thought that you could handle key combinations before
>>>>> the TextEdit already using Keys.onPressed. If you have that and a
>>>>> global clipboard, why do you need the filter element?
>>>>>
>>>>> Even if there is a need, I don't see why you need this to be a
>>>>> separate filter instead of just having the signals on the
>>>>> TextArea/TextInput (like how QTextEdit has a virtual you can use).
>>>> The API is proposed for Ubuntu UI toolkit and therefore we are working
>>>> on top of existing Qt5 QML components. I brought it here to discuss it
>>>> so that - eventually - we can include it in QML or Qt Components.
>>>>
>>>> On top of TextInput/TextEdit I don't get Key_Copy/Key_Cut/Key_Paste keys
>>>> in Keys.onPressed, so this functionality must be captured somehow inside
>>>> these components, otherwise we need a component to filter these out and
>>>> steal them from the TextEdit/TextInput.
>>> In general I think explicit key handling on an item is supposed to
>>> take precedence over the default key handling. Which means it should
>>> work, but we might need to 'fix a few bugs'.
>> Then, if we should get these key events, I guess I have to file a bug.
>> In C++ I get them as QKeySequence matches, are those converted into
>> Qt.Key_XXX codes? (haven't checked yet the sources)
> My guess is that they aren't being converted, and that's the bug.
I'll file a bug for that then.
>
>>>> On the other hand I do also see a benefit on having these events
>>>> captured on TextInput/TextEdit level so the functionality would then
>>>> also be available in TextField and TextArea respectively.
>>>>
>>>>>> this feels like something that would make sense as part of MimeData above,
>>>>>> perhaps?
>>>>>>
>>>>>> Not really, the MimeData aint supposed to be instantiable within QML. It's
>>>>>> just a data holder.
>>>>>>
>>>>>>
>>>>>> also, what sort of filtering do you forsee being done?
>>>>>>
>>>>>> Typical example is when you want to filter the data to be copied from the
>>>>>> clipboard into a text input element. You can control the content when using
>>>>>> context menu so you can leave out all the unnecessary data, but when using
>>>>>> key sequence, both TextInput and TextEdit will grab those so you are no
>>>>>> longer in control of filtering the data pasted. The ClipboardFilter helps
>>>>>> you on that.
>>>>>>
>>>>>> Yes, you could say that you push the data filtered, but at the source you
>>>>>> never know who's gonna be the destination.
>>>>>>
>>>>> To sum up, since I suggested that this all belongs on one singleton
>>>>> clipboard object, here's what that API might look like:
>>>>>
>>>>> Clipboard { // Although you can't instantiate one yourself
>>>>>     property string data - latest data (of a single mimetype which is
>>>>> convertable to string) in the clipboard
>>>>>     property string mimeType - the mimetype of data
>>>>>
>>>>>     //Possible conveniences
>>>>>     readonly property list<string> formats - contains the available
>>>>> types in the clipboard
>>>>>     readonly property string text - text data or undefined if no text
>>>>> data in clipboard
>>>>>     readonly property string html - HTML data or undefined
>>>>>     readonly property list<url> urls - list or URLs or undefined
>>>>>     readonly property color color - color data or undefined
>>>>> }
>>>>>
>>>>> Note that bytearray MIME data, which QML can't handle, is basically
>>>>> ignored. You'll need C++ if you want to use that.
>>>> Right. Almost like my second proposal :) But, when to push data to
>>>> clipboard?
>>> Any time you set data or mimeData.
>> You mean mimeType is set. To me this sounds like one operation overhead.
>> If for instance you have a clipboard history logger app like Clipit, it
>> will show two separate clipboard entries. That may be a bit of a problem.
>>>> When the data is set or when the type is set?
>>> Any time the clipboard changes, the new data and type are set.
>>>
>>>> How do we make
>>>> sure we push the right time?
>>> We push every time. This means we'll push at the right time, as well
>>> as many other times. Is that a problem?
>> It is. Same reason I had for Clipit. You will have plenty of history
>> entries, and I'd say we should consider these cases too. It also sounds
>> a bit of a hammering ;)
>>>> How about if we want to have more MIME
>>>> types in the clipboard at the same time?
>>> This doesn't support that. Is that really an issue considering how few
>>> MIME types (only string ones) can be effectively used in QML?
>> An application may want to store different data in the clipboard.
>> Typical case is the QtCreator copy case: QtCreator stores the plain text
>> and HTML formatted text too in clipboard.
>>>> In addition to your summary I had a function called push() which when
>>>> called would copy all the properties set to the clipboard. Note that I
>>>> have few properties more R/W than you.
>>>>
>>>> Clipboard { // Although you can't instantiate one yourself
>>>>     property string data - latest data (of a single mimetype which is
>>>> convertable to string) in the clipboard
>>>>     property string mimeType - the mimetype of data
>>>>
>>>>     //Possible conveniences
>>>>     readonly property list<string> formats - contains the available
>>>> types in the clipboard
>>>>     property string text - text data or undefined if no text
>>>> data in clipboard
>>>>     property string html - HTML data or undefined
>>>>     readonly property list<url> urls - list or URLs or undefined
>>>>     property color color - color data or undefined
>>>>
>>>>     // functions
>>>>     function push() - copies the data to the clipboard
>>>> }
>>>>
>>> I don't like having the push() pushing the data from the Clipboard
>>> singleton to the backing clipboard. The Clipboard is supposed to
>>> represent the state of the application clipboard, there's no way to
>>> tell if the text is one that just came in from somewhere else or one
>>> that you set but haven't pushed yet.
>>>
>>> If you need this manual control of pushing data, and multiple MIME
>>> type support, the API should look more like this:
>>>
>>> Clipboard {
>>>     property MimeData data
>>> }
>> Yep, somewhat similar to what I had previously, just with singleton type
>> and one property, which looks a bit too funny :)
> It doesn't look quite so funny in usage. It would be used like this
>
> text: Clipboard.data.html;
>
> or
>
> Clipboard.data = MimeData(textArea.text);
>
> although I now think it would be better to make data read-only and
> have a separate push(variant) property, so that you can do
> Clipboard.push(textArea.text) and not have to worry about constructing
> a MimeData (in the common case.). Only when you actually want to push
> more complex data do you pass a MimeData in there. It also sounds like
> we do not want to allow bindings which set Clipboard.data.
>
>>> where setting data pushes a new MimeData and reading it grabs the
>>> current data. Just like the basic QClipboard API (in fact, we probably
>>> want them to work with the same QMimeData objects).
>>>
>>> We lose a lot of convenience though, we'd at least need a .toString()
>>> and convenience string constructor on MimeData so QML can, in the
>>> common case, just use it as strings.
>>>
>>>>> For the filter properties, we just need something like Keys.onPressed:
>>>>> if (key.buttons == Qt::Copy_Shortcut), right? Other than the default
>>>>> key handling, pastes would already be programmatic (thus
>>>>> intercept-able by the application).
>>>> Till these get into Qt5 we have to survive with some filters, and most
>>>> likely we will embed those into TextField and TextArea somehow. As said,
>>>> I cannot intercept the shortcuts at the moment outside of
>>>> TextInput/TextEdit.
>>> I thought you were suggesting a Clipboard API to put in Qt5 anyways,
>>> which would mean the ETA for both filters and the TextInput/TextEdit
>>> changes would likely be the same. This is definitely the sort of
>>> functionality which would fit better in QtQuick than each component
>>> set having an "API-Compatible" implementation.
>> We need this API now. So I was thinking to discuss an API that fits both
>> of us, so when QtQuick will have it we can drop ours from our libraries.
>>
>> So: the API starts to look more like:
>>
>> 1. Because it would be beneficial to be able to push several mime types
>> at the same time
>> Clipboard {
>>     property MimeData data;
>> }
> Clipboard {
>     read-only property MimeData data;
>     function push(variant); //For convenient pushing of strings, and
> to prevent attempts to set data.text (which may not trigger a push).
> }
push() getting a variant would also accept MimeData I guess, right? So
if a MimeData is pushed, it will be placed into the system clipboard as
it comes, and in case other types are pushed, we resolve that internally
and push.
>> 2. The MimeData would contain properties to get/set different mime data
>> types (we could actually derive this from QMimeData)
>> MimeData {
>>     readonly property list<string> formats
>>     property string data
>>     property string type
>>     // convenience properties to set certain types
>>     property string text - to get/set "text/plain"
>>     property string html - to get/set "text/html"
>>     property color color - to get/set color data
>>     property list<url> urls - to get/set urls
>> }
> If this is going to come from QMimeData, it would need color to be of
> variant type and I don't think we should have data (we can't convert
> arbitrary byte arrays to strings). Then we can add the property
> declarations and signals (no new functions) to QMimeData and expose it
> directly.
It would be beneficial to derive from it as it could be pushed straight
to clipboard, however the clipboard takes the ownership over the
QMimeData instance, so it would mess up ownerships. So in that sense we
could also keep color property as is, we can do conversion inside the
implementation - but even if we would derive from QMimeData we could do
that.
>
>> 3. Clipboard shortcut handling should be doable on TextInput/TextEdit
>> level by simply handling Keys.onPressed attached signal (key codes:
>> Key_Cut, Key_Copy, Key_Paste)
>>
>> How does this sound?
> My only remaining concern is about whether it's convenient enough to
> do text: Clipboard.data.text instead of having convenience read-only
> properties on Clipboard. But it's a minor point, so it could be added
> to the Clipboard API later.
Not a bad idea. I'd say we could have them now as right now the type
looks pretty empty, and at this point of implementation is easier to add
interfaces than later.

Zsombor



More information about the Qt-components mailing list