How to maintain widgets aspect ratio in Qt? - user-interface

How is it possible to maintain widgets aspect ratio in Qt and what about centering the widget?

You don't have to implement your own layout manager. You can do with inheriting QWidget and reimplementing
int QWidget::heightForWidth( int w ) { return w; }
to stay square. However, heightForWidth() doesn't work on toplevel windows on X11, since apparently the X11 protocol doesn't support that. As for centering, you can pass Qt::AlignCenter as the third parameter of QBoxLayout::addWidget() or the fifth parameter of QGridLayout::addWidget().
Note: In newer versions of Qt at least, QWidget does not have the heightForWidth or widthForHeight anymore (so they cannot be overriden), and therefore setWidthForHeight(true) or setHeightForWidth(true) only have an effect for descendants of QGraphicsLayout.

The right answer is to create your custom layout manager. That is possible by subclassing QLayout.
Methods to implement when subclassing QLayout
void addItem(QLayoutItem* item);
Adds item to layout.
int count() const;
Returns the item count.
QLayoutItem* itemAt(int index) const;
Returns item reference at index or 0 if there's none.
QLayoutItem* takeAt(int index);
Takes and returns item from the layout from index or returns 0 if there is none.
Qt::Orientations expandingDirections() const;
Returns the layouts expanding directions.
bool hasHeightForWidth() const;
Tells if the layout handles height for width calculation.
QSize minimumSize() const;
Returns the layouts minimum size.
void setGeometry(const QRect& rect);
Sets the geometry of the layout and the items inside it. Here you have to maintain the aspect ratio and do the centering.
QSize sizeHint() const;
Returns the preferred size for the layout.
Further reading
Maintaining square form for a widget in Qt # Forum Nokia
Implementing a layout manager in Qt # Forum Nokia
Writing custom layout managers # Qt documentation

Calling resize() from within resizeEvent() has never worked well for me -- at best it will cause flickering as the window is resized twice (as you have), at worst an infinite loop.
I think the "correct" way to maintain a fixed aspect ratio is to create a custom layout. You'll have to override just two methods, QLayoutItem::hasHeightForWidth() and QLayoutItem::heightForWidth().

I too was trying to achieve the requested effect: a widget that keeps a fixed aspect ratio while staying centred in its allocated space. At first I tried other answers from this question:
implementing heightForWidth and hasHeightForWidth as suggested by marc-mutz-mmutz simply didn't work for me.
I briefly looked at implementing a custom layout manager, but all Bleadof's links were dead, and when I found the documentation and read through it, it looked way too complicated for what I was trying to achieve.
I ended up creating a custom widget that responds to resizeEvent and uses setContentsMargin to set margins such that the remaining content area keeps the desired ratio.
I found I also had to set the widget's size policy to QSizePolicy::Ignored in both directions to avoid odd resizing issues resulting from the size requests of child widgets—the end result is that my widget accepts whatever size its parent allocates to it (and then sets its margins as described above to keep the desired aspect ratio in its content area).
My code looks like this:
from PySide2.QtWidgets import QWidget, QSizePolicy
class AspectWidget(QWidget):
'''
A widget that maintains its aspect ratio.
'''
def __init__(self, *args, ratio=4/3, **kwargs):
super().__init__(*args, **kwargs)
self.ratio = ratio
self.adjusted_to_size = (-1, -1)
self.setSizePolicy(QSizePolicy(QSizePolicy.Ignored, QSizePolicy.Ignored))
def resizeEvent(self, event):
size = event.size()
if size == self.adjusted_to_size:
# Avoid infinite recursion. I suspect Qt does this for you,
# but it's best to be safe.
return
self.adjusted_to_size = size
full_width = size.width()
full_height = size.height()
width = min(full_width, full_height * self.ratio)
height = min(full_height, full_width / self.ratio)
h_margin = round((full_width - width) / 2)
v_margin = round((full_height - height) / 2)
self.setContentsMargins(h_margin, v_margin, h_margin, v_margin)
(Obviously, this code is in Python, but it should be straightforward to express in C++ or your language of choice.)

In my case overriding heightForWidth() doesn't work. And, for someone, it could be helpful to get working example of using resize event.
At first subclass qObject to create filter. More about event filters.
class FilterObject:public QObject{
public:
QWidget *target = nullptr;//it holds a pointer to target object
int goalHeight=0;
FilterObject(QObject *parent=nullptr):QObject(parent){}//uses QObject constructor
bool eventFilter(QObject *watched, QEvent *event) override;//and overrides eventFilter function
};
Then eventFilter function. It's code should be defined outside of FilterObject definition to prevent warning. Thanks to this answer.
bool FilterObject::eventFilter(QObject *watched, QEvent *event) {
if(watched!=target){//checks for correct target object.
return false;
}
if(event->type()!=QEvent::Resize){//and correct event
return false;
}
QResizeEvent *resEvent = static_cast<QResizeEvent*>(event);//then sets correct event type
goalHeight = 7*resEvent->size().width()/16;//calculates height, 7/16 of width in my case
if(target->height()!=goalHeight){
target->setFixedHeight(goalHeight);
}
return true;
};
And then in main code create FilterObject and set it as EventFilter listener to target object. Thanks to this answer.
FilterObject *filter = new FilterObject();
QWidget *targetWidget = new QWidget();//let it be target object
filter->target=targetWidget;
targetWidget->installEventFilter(filter);
Now filter will receive all targetWidget's events and set correct height at resize event.

Related

ScalaFx children hierarchy and casting / instance reference

I'm wandering if this is the optimal way of doing it with ScalaFx: A GUI is composed of bunch of nodes, to which I suck content from a SQL-DB. Main Pane is a FlowPane populated with few hundred elements. Each element is composed of four level hierarchy (see numbers describing the levels):
1 2 3 4
VBox -+-> VBox ---> StackPane -+-> ImageView
+-> Label +-> Rectangle
As far as I have experienced the I can access the nodes and their attributes in different levels. Ie. I can give user feedback by changing the Rectangle color below the ImageView Node as the compound element is chosen by mouse click or by ContextMenu.
I could access the Rectangle attributes directly, but it is easy to make mistakes as the list references children.get(0) are directly dependent from order of the children as the nodes are positioned in parent.
val lvone = vbnode.children // VBox (main)
val lvtwo = lvone.get(0) // VBox
val lvthree = lvtwo.asInstanceOf[javafx.scene.layout.VBox].children.get(0) // StackPane
val lvfour = lvthree.asInstanceOf[javafx.scene.layout.StackPane].children.get(0) // Rectangle
if (lvfour.isInstanceOf[javafx.scene.shape.Rectangle]) lvfour.asInstanceOf[javafx.scene.shape.Rectangle].style = "-fx-fill: #a001fc;"
println("FOUR IS:"+lvfour.getClass)
Here's sample to demonstrate the "safer" access to the elements in node hierarchy (node hierarchy creation is in rather annoying structure of code, so it is not included):
val levelone = vbnode.children
println("LV1 Node userData:"+vbnode.userData) // my database reference for the main / container element
println("LV1 Parent children class:"+levelone.get(0).getClass) // class javafx.scene.layout.VBox
for (leveltwo <- levelone) {
println("LV2 Children Class:"+leveltwo.getClass)
println("LV2 Children Class Simple Name:"+leveltwo.getClass.getSimpleName) // VBox
if (leveltwo.getClass.getSimpleName == "VBox") {
leveltwo.style = "-fx-border-width: 4px;" +
"-fx-border-color: blue yellow blue yellow;"
for (levelthree <- leveltwo.asInstanceOf[javafx.scene.layout.VBox].children) {
println("LV3 children:"+levelthree.getClass.getName)
if (levelthree.getClass.getSimpleName == "StackPane") {
for (levelfour <- levelthree.asInstanceOf[javafx.scene.layout.StackPane].children) {
println("LV4 children:"+levelfour.getClass.getName)
if (levelfour.getClass.getSimpleName == "Rectangle") {
if (levelfour.isInstanceOf[javafx.scene.shape.Rectangle]) println("Rectangle instance confirmed")
println("LV4 Found a Rectangle")
println("original -fx-fill / CSS:"+ levelfour.asInstanceOf[javafx.scene.shape.Rectangle].style)
levelfour.asInstanceOf[javafx.scene.shape.Rectangle].style = "-fx-fill: #a001fc;"
} // end if
} // end for levelfour
} // end if
} // end for levelthree
} // end if
} // end for leveltwo
Questions:
Is there smarter way to do the type casting of node types, since only javafx API based references are acceptable (BTW I'm using ScalaIDE)? Options I am using are:
1- simple / shortcut way: evaluation by using leveltwo.getClass.getSimpleName == "VBox" , which is the shortcut from API jungle. But is it efficient and safe?
2- cluttering way by using probably the by the book style:
if (levelfour.isInstanceOf[javafx.scene.shape.Rectangle])
Other question: Now in reference to the fully qualified reference based on javafx ie. javafx.scene.shape.Rectangle, I would like to use scala reference instead, but I get an error which enforces me to adopt the javafx based reference. Not a big deal as I can use javafx reference, but I wander if there is scalafx based option?
Happy to get constructive feedback.
If I understand you correctly, you seem to be wanting to navigate the nodes of a sub-scene (that belongs to a higher-level UI element construct) in order to change the appearance of some of the nodes within it. Do I have that right?
You raise a number of different issues, all within the one question, so I'll do my best to address them all. As a result, this is going to be a long answer, so please bear with me. BTW, In future, it would help if you ask one question for each issue. ;-)
Firstly, I'm going to take your problem at face value: that you need to browse through a scene in order to identify a Rectangle instance and change its style. (I note that your safe version also changes the style of the second VBox, but I'm going to ignore that for the sake of simplicity.) This is a reasonable course of action if you have little to no control over the structure of each element's UI. (If you directly control this structure, there are far better mechanisms, which I'll come to later.)
At this point, it might be worth expanding on the relationship between ScalaFX and JavaFX. The former is little more than a set of wrappers for the latter, to give the library a Scala flavor. In general, it works like this: the ScalaFX version of a UI class takes a corresponding JavaFX class instance as an argument; it then applies Scala-like operations to it. To simplify things, there are implicit conversions between the ScalaFX and JavaFX instances, so that it (mostly) appears to work by magic. However, to enable this latter feature, you must add the following import to each of your source files that reference ScalaFX:
import sclafx.Includes._
For example, if JavaFX has a javafx.Thing (it doesn't), with setSize and getSize accessor methods, then the ScalaFX version would look like this:
package scalafx
import javafx.{Thing => JThing} // Rename to avoid confusion with ScalaFX Thing.
// ScalaFX wrapper for a Thing.
class Thing(val delegate: JThing) {
// Axilliary default constructor. Let's assume a JThing also has a default
// constructor.
//
// Creates a JavaFX Thing when we don't have one available.
def this() = this(new JThing)
// Scala-style size getter method.
def size: Int = delegate.getSize
// Scala-style size setter method. Allows, say, "size = 5" in your code.
def size_=(newSize: Int): Unit = delegate.setSize(newSize)
// Etc.
}
// Companion with implicit conversions. (The real implementation is slightly
// different.)
object Thing {
// Convert a JavaFX Thing instance to a ScalaFX Thing instance.
implicit def jfxThing2sfx(jThing: JThing): Thing = new Thing(jThing)
// Convert a ScalaFX Thing instance to a JavaFX Thing instance.
implicit def sfxThing2jfx(thing: Thing): JThing = thing.delegate
}
So, quite a lot of work for very little gain, in all honesty (although ScalaFX does simplify property binding and application initialization). Still, I hope you can follow me here. However, this allows you to write code like the following:
import javafx.scene.shape.{Rectangle => JRectangle} // Avoid ambiguity
import scalafx.Includes._
import scalafx.scene.shape.Rectangle
// ...
val jfxRect: JRectangle = new JRectangle()
val sfxRect: Rectangle = jfxRect // Implicit conversion to ScalaFX rect.
val jfxRect2: JRectangle = sfxRect // Implicit conversion to JavaFX rect.
// ...
Next, we come to type checking and casting. In Scala, it's more idiomatic to use pattern matching instead of isInstanceOf[A] and asInstanceOf[A] (both of which are frowned upon).
For example, say you have a Node and you want to see if it is actually a Rectangle (since the latter is a sub-class of the former). In the style of your example, you might write something like the following:
def changeStyleIfRectangle(n: Node): Unit = {
if(n.isInstanceOf[Rectangle]) {
val r = n.asInstanceOf[Rectangle]
r.style = "-fx-fill: #a001fc;"
}
else println("DEBUG: It wasn't a rectangle.")
}
The more idiomatic Scala version of the same code would look like this:
def changeStyleIfRectangle(n: Node): Unit = n match {
case r: Rectangle => r.style = "-fx-fill: #a001fc;"
case _ => println("DEBUG: It wasn't a rectangle.")
}
This may seem a little finicky, but it tends to result in simpler, cleaner code, as I hope you'll see. In particular, note that case r: Rectangle only matches if that is the real type of n, and it then casts n to r as a Rectangle.
BTW, I would expect that comparing types is more efficient than getting the name of the class, via getClass.getSimpleName and comparing to a string, and there's less chance of error. (For example, if you mistype the class name of the string you're comparing to, e.g. "Vbox", instead of "VBox", then this will not result in a compiler error, and the match will always fail.)
As you point out, your direct approach to identifying the Rectangle is limited by the fact that it requires a very specific scene structure. If you change how each element is represented, then you must change your code accordingly, or you'll get a bunch of exceptions.
So let's move on to your safe approach. Clearly, it's going to be a lot slower and less efficient than the direct approach, but it still relies upon the structure of the scene, even if it's less sensitive to the order in which the children are added at each level of hierarchy. If we change the hierarchy, it will likely stop working.
Here's an alternative approach that uses the class hierarchy of the library to assist us. In a JavaFX scene, everything is a Node. Furthermore, nodes that have children (such as VBox and StackPane) are subclasses of Pane as well. We'll use a recursive function to browse the elements below a specified starting Node instance: every Rectangle it encounters will have its style changed.
(BTW, in this particular case, there are some issues with implicit conversions, which makes a pure ScalaFX solution a little cumbersome, so I'm going to match directly on the JavaFX versions of the classes instead, renamed to avoid any ambiguity with the equivalent ScalaFX types. The implicit conversions will work fine when calling this function.)
import javafx.scene.{Node => JNode}
import javafx.scene.layout.{Pane => JPane}
import javafx.scene.shape.{Rectangle => JRectangle}
import scala.collection.JavaConverters._
import scalafx.Includes._
// ...
// Change the style of any rectangles at or below starting node.
def setRectStyle(node: JNode): Unit = node match {
// If this node is a Rectangle, then change its style.
case r: JRectangle => r.style = "-fx-fill: #a001fc;"
// If the node is a sub-class of Pane (such as a VBox or a StackPane), then it
// will have children, so apply the function recursively to each child node.
//
// The observable list of children is first converted to a Scala list to simplify
// matters. This requires the JavaConverters import above.
case p: JPane => p.children.asScala.foreach(setRectStyle)
// Otherwise, just ignore this particular node.
case _ =>
}
// ...
A quick few observations on this function:
You can now use any hierarchy of UI nodes that you like, however, if you have more than one Rectangle node, it will change the style of all of them. If this doesn't work for you, you could add code to check other attributes of each Rectangle to determine which one to modify.
The asScala method is used to convert the children of the Pane node to a Scala sequence, so we can then use the foreach higher-order function to recursively pass each child in turn to the setRectStyle method. asScala is made available by the import scala.collection.JavaConverters._ statement.
Because the function is recursive, but the recursive call is not in the tail position (the last statement of the function), it is not tail-recursive. What this means is if you pass a huge scene to the function, you might get a StackOverflowException. You should be fine with any reasonable size of scene. (However, as an exercise, you might want to write a tail-recursive version so that the function is stack safe.)
This code is going to get slower and less efficient the bigger the scene becomes. Possibly not your top concern in UI code, but a bad smell all the same.
So, as we've seen, having to browse through a scene is challenging, inefficient and potentially error prone. Is there a better way? You bet!
The following will only work if you have control over the definition of the scene for your data elements. If you don't, you're stuck with solutions based upon the above.
The simplest solution is to retain a reference to the Rectangle whose style you want to change as part of a class, then access it directly as needed. For example:
import scalafx.Includes._
import scalafx.scene.control.Label
import scalafx.scene.layout.{StackPane, VBox}
import scalafx.scene.shape.Rectangle
final class Element {
// Key rectangle whose style is updated when the element is selected.
private val rect = new Rectangle {
width = 600
height = 400
}
// Scene representing an element.
val scene = new VBox {
children = List(
new VBox {
children = List(
new StackPane {
children = List(
// Ignore ImageView for now: not too important.
rect // Note: This is the rectangle defined above.
)
}
)
},
new Label {
text = "Some label"
}
)
}
// Call when element selected.
def setRectSelected(): Unit = rect.style = "-fx-fill: #a001fc;"
// Call when element deselected (which I assume you'll require).
def setRectDeselected(): Unit = rect.style = "-fx-fill: #000000;"
}
Clearly, you could pass a data reference as an argument to the class and use that to populate the scene as you like. Whenever you need to change the style, calling one of the two latter functions achieves what you need with surgical precision, no matter what the scene structure looks like.
But there's more!
One of the truly great features about ScalaFX/JavaFX is that it has observable properties that can be used to make the scene manage itself. You will find that most fields on a UI node are of some type "Property". What this allows you to do is to bind a property to the field, such that when you change the property, you change the scene accordingly. When combined with event handlers, the scene takes care of everything all by itself.
Here, I've reworked the latter class. Now, it has a handler that detects when the scene is selected and deselected and reacts by changing the property that defines the style of the Rectangle.
import scalafx.Includes._
import scalafx.beans.property.StringProperty
import scalafx.scene.control.Label
import scalafx.scene.input.MouseButton
import scalafx.scene.layout.{StackPane, VBox}
import scalafx.scene.shape.Rectangle
final class Element {
// Create a StringProperty that holds the current style for the Rectangle.
// Here we initialize it to be unselected.
private val unselected = "-fx-fill: #000000;"
private val selected = "-fx-fill: #a001fc;"
private val styleProp = new StringProperty(unselected)
// A flag indicating whether this element is selected or not.
// (I'm using a var, but this is heavily frowned upon. A better mechanism might be
// required in practice.)
private var isSelected = false
// Scene representing an element.
val scene = new VBox {
children = List(
new VBox {
children = List(
new StackPane {
children = List(
// Ignore ImageView for now: not too important.
// Key rectangle whose style is bound to the above property.
new Rectangle {
width = 600
height = 400
style <== styleProp // <== means "bind to"
}
)
}
)
},
new Label {
text = "Some label"
}
)
// Add an event handler. Whenever the VBox (or any of its children) are
// selected/unselected, we just change the style property accordingly.
//
// "mev" is a "mouse event".
onMouseClicked = {mev =>
// If this is the primary button, then change the selection status.
if(mev.button == MouseButton.Primary) {
isSelected = !isSelected // Toggle selection setting
styleProp.value = if(isSelected) selected
else unselected
}
}
}
}
Let me know how you get on...

Questions regarding GetWindowPlacement return data

I'm a bit unsure of the meaning of some of the return values from a call to the GetWindowPlacement() function, so I'd like your help, please.
I'll be calling this to obtain the normal dimensions of a hidden window.
First, where do the values of the showCmd field come from? In the Microsoft documentation of the return structure (WINDOWPLACEMENT structure, all the descriptions of the possible values use verbs/action words; e.g., "SW_MAXIMIZE: Maximizes the specified window", or "SW_SHOWNOACTIVATE: Displays a window in its most recent size and position."
I want to obtain the dimensions of the hidden window without unhiding/restoring it first, so with the verbs it seems that I would have to call SetWindowPlacement() with showCmd set to SW_SHOWNOACTIVATE before calling GetWindowPlacement. Is that correct?
So do I understand correctly that the primary (and perhaps only) way that field gets its various values is by an explicit call to SetWindowPlacement() somewhere?
My second question relates to the rcNormalPosition return values. Do those data include the window decorations, or are they client values?
Thank you for your time!
The meaning of the showCmd member of the WINDOWPLACEMENT struct is a bit confusing because Win32 is reusing the SW_* commands used by ShowWindow().
Luckily, the meaning is documented on the GetWindowPlacement() function.
If the window identified by the hWnd parameter is maximized, the
showCmd member is SW_SHOWMAXIMIZED. If the window is minimized,
showCmd is SW_SHOWMINIMIZED. Otherwise, it is SW_SHOWNORMAL.
So, based on which of those 3 values is returned, you can tell whether the window is currently maximized, minimized or, normal (restored). And if you'd like to know what the normal placement is, you can just use the rcNormalPosition member. You do not need to call SetWindowPlacement() at all.
However, heed the warning that GetWindowPlacement() returns workspace coordinates rather than screen coordinates, which differ based on taskbar position and size. This is not a problem if you are only using the coordinates returned by GetWindowPlacement() to call SetWindowPlacement(). Otherwise, you might have to find a way to convert from workspace to screen coordinates.
I found these 2 functions to work for me.
void MyDialog::LoadDialogPlacement()
{
static WINDOWPLACEMENT last_wp = {};
// Load last stored DB version
WINDOWPLACEMENT *wp = new WINDOWPLACEMENT;
GetStoredWindowPlacement(&wp);
if (memcmp((void *)&last_wp, (const void *)wp, sizeof(WINDOWPLACEMENT)) == 0) return;
memcpy((void *)&last_wp, (const void *)wp, sizeof(WINDOWPLACEMENT));
SetWindowPlacement(wp);
delete[] wp;
}
void MyDialog::SaveDialogPlacement()
{
static WINDOWPLACEMENT last_wp = {};
if (IsWindowVisible())
{
WINDOWPLACEMENT wp = {};
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(&wp);
if (memcmp((void *)&last_wp, (const void *)&wp, wp.length) == 0) return;
memcpy((void *)&last_wp, (const void *)&wp, wp.length);
StoreWindowPlacement(&wp);
}
}

BlackBerry - What does getPreferredWidth() and getPreferredHeight() return?

I am not getting clear about the concept of getPreferredWidth() and getPreferredHeight() methods. What do they return means on what what basis the values are returned.??
What is difference between getWidth() and getPreferredWidth()???
Thank you..
By default, Field#getPreferredHeight() and Field#getPreferredWidth() return 0. They're there for your custom Fields to override with whatever your custom Field's preferred width/height is.
The value you supply in your overridden methods is then taken into consideration when it comes to laying out your Field. If your preferred values are suitable for the display area and your Field's content, then your preferred values will be used. These values are just suggestions.
Field#getHeight() and Field#getWidth() on the other hand return the actual extent of the Field.
Field#getPreferredWidth() is used to determine width of the specific field when it is created. You need to override this method if you want to create some customization. Display#getWidth() returns the physical screen size of your device or simulator which you can use to create buttons relative to the screen width. Field#getWidth() returns the actual width of your field that you created earlier. I used that code block to create a button which fills whole screen width and with default height.
This is how you override the getPreferredWidth() method for a specific field i.e. ButtonField.
ButtonField myButton = new ButtonField("Click", USE_ALL_WIDTH){
public int getPreferredWidth(){
return Display.getWidth();
}
};

Qt4: QAbstractTableModel Drag and Drop w/o MIME

I have a class which inherits QAbstractTableModel, and holds some complex structs in a QMap. The QVariant data(QModelIndex index, ...) method just returns an enum which describes how a custom item delegate should draw the contents of a cell. I would like to implement drag and drop functionality in this model so that users can reorder these structs in the QMap, but can't quite figure our how Qt would like me to do this. All I need is to see the source and destination indices of the drag/drop operation and I can take care of the rest, but the closest thing I've found in QAbstractItemModel is the dropMimeData() function. DropMimeData() doesn't give me the source index and requires me to convert the data into some MIME type (plaintext, etc.), which it is definitely not. I can hack my way through this by creating a QMimeData that just contains the source index, but I would like to really learn to use Qt as it's meant to be used, and I feel like I'm missing something. Any thoughts?
Just to help clarify a bit, the application is an animation program which acts sort of like Adobe Flash. The class which inherits QAbstractTableModel has a QMap<int, FrameState> (with struct FrameState{QPointF pos; bool visible;}) to hold keyframes. This state of this QMap is what I would like to display and have users edit. I draw a green circle if there is a visible key frame, a red circle if there is an invisible keyframe, a line if the previous keyframe was visible, and nothing if the previous keyframe was invisible. I would like users to be able to drag the keyframes around to change their QMap key.
Thanks!
You can use the views dragEnterEvent to get the indices that were selected initially:
void DropTreeView::dragEnterEvent(QDragEnterEvent *event)
{
QTreeView::dragEnterEvent(event);
const QItemSelectionModel * sm = selectionModel();
if (!sm)
return;
dragStartIndicies = sm->selectedIndexes();
}
You'll need to use the mime-types for the drag and drop, but C++ Qt provides a nice way to do that using QDataStream:
QMimeData *YourModel::mimeData( const QModelIndexList &indexes ) const
{
QByteArray encodedData;
QDataStream stream( &encodedData, QIODevice::WriteOnly );
stream << yourQMap /* OR almost any Qt data structure */;
QMimeData *mData = new QMimeData();
mData->setData( YOUR_MIME_TYPE, encodedData );
return mData;
}
On the receiving end, you can get your data structure (i.e. QMap if that's what you want to use) back out of the QDataStream:
QByteArray encodedData = yourMimeData->data( YOUR_MIME_TYPE );
QDataStream stream( &encodedData, QIODevice::ReadOnly );
QMap decodedMap;
stream >> decodedMap;

Blackberry -- Updating screen changes drawing order of manager its field elements

Scenario
In a screen I have 2 managers: 1) menu manager at the top and 2) body manager that has info/button elements. The menu manager does custom drawing so that its menu elements (LabelFields) are properly spaced.
Core Issue - Manager and subfield drawing order
The screen draws fine except when the user preforms an action (clicks a button) that results in an element withing the body manager being added/removed. Once field elements are added/removed from the body, the order in which the menu is drawn gets mixed up.
When the body manager adds or removes a field, instead of the menu manager drawing itself and then its sub elements (label fields), the menu manager begins to draw its sub elements and then itself; thus painting on top of the label fields and making them look like they've disappeared.
Comments
Already tried invalidate and other options -- I've tried to call invalidate, invalidateall, updateDisplay... after adding/removing field elements from body. All without success.
Removing custom sublayout works -- The only way that I can resolve this issue is to remove the menu managers custom sublayout logic. Unfortunately the menu system then draws in a traditional manner and does not provide enough spacing.
Below is the sublayout code for the menu manager, am I missing something here?
public void sublayout(int iWidth, int iHeight)
{
final int iNumFields = getFieldCount();
int maxHeight = 0;
final int segmentWidth = iWidth / iNumFields;
final int segmentWidthHalf = segmentWidth / 2;
for (int i = 0; i < iNumFields; i++)
{
final Item currentField = (Item)this.getField(i);
// 1. Use index to compute bounds of the field
final int xSegmentTrueCenter = segmentWidth * i + segmentWidthHalf;
// 2. center field inbetween bounds using field width (find fill width of text)
final int xFieldStart = xSegmentTrueCenter - currentField.getFont().getAdvance(currentField.getText())/2;
// set up position
setPositionChild(currentField, xFieldStart, getContentTop() + MenuAbstract.PADDING_VERTICAL);
// allow child to draw itself
layoutChild(currentField, iWidth, currentField.getHeight());
// compute max height of the field
//int fieldheight = currentField.getHeight();
maxHeight = currentField.getHeight() > maxHeight
? currentField.getHeight() + 2 * MenuAbstract.PADDING_VERTICAL
: maxHeight;
}
this.setExtent(iWidth, maxHeight);
}
Final Questions
Ultimately I want to keep the custom layout of the menu manager while being allowed to redraw field elements. Here are my final questions:
Have you experienced this before?
Why would the menu manager begin drawing in the wrong order when a field element is added/remove to the screen?
Does the native Manager.sublayout() do something that I'm not to maintain drawing order?
I haven't seen the behavior you describe, but the following line is a little troubling:
// allow child to draw itself
layoutChild(currentField, iWidth, currentField.getHeight());
getHeight() shouldn't return a sensible value until the field has had setExtent called through the layoutChild method. Though I'd expect that it would cause problems in all cases - not sure why this would work the first time around. In your logic I think you can safely just use iHeight instead of currentField.getHeight() in that line. The field will only make itself as big as it needs to be - it won't use all of iHeight unless it's something like a VerticalFieldManager

Resources