Which is the most generic view in Qt Quick Controls 2 - qml

I'm learning to work with QML and Qt Quick Controls 2 and try to figure out how to write "proper" applications with it (endgame is a small prototype for embedded devices).
One thing I'm missing is a simple and explicit way to build multi-page applications: there is StackView, TabView and SwipeView, but there is nothing like SimpleView, a component that I could put Page components into and then switch them via custom actions. Currently, I'm mis-using the SwipeView to achieve something similar, by setting interactive property to false, but I have to wonder whether this is a proper way.
So, which is the most generic "page container" component in Qt Quick Controls 2?

Take a look at StackLayout from Qt Quick Layouts. It's a stack of arbitrary items, where you can control the index of the currently visible item.
StackLayout {
anchors.fill: parent
currentIndex: 1
Page {
// ...
}
Page {
// ...
}
}

Related

How to access components inside a custom ToolWindow from an action?

I have registered an action in the EditorPopupMenu (this is right click menu). I also have a bunch of components inside a ToolWindow (that I designed using the GUI Designer plugin) that I want to update the values of.
There have been some posts on the IntelliJ forums about this, and the typical answer seems to advice using the ToolWindow's ContentManager, and obtain the JPanel containing all your components. E.g. the following:
Project p = e.getProject();
ToolWindow toolWindow;
toolWindow = ToolWindowManager.getInstance(p).getToolWindow("My ToolWindow ID");
ContentManager contentManager = toolWindow.getContentManager();
JPanel jp = (JPanel) contentManager.getContent(0).getComponent();
This feels counterintuitive... Having to navigate inside JPanel's to find a bunch of components. What if I decided to put my components inside a different container? Suddenly the way I navigate to my components would break down.
Is it really the most practical way to constrain myself to the way my GUI is built? Can't I access these components in a different way?
I found a way to access my custom myToolWindow. This should help quite some people.
Make sure that your custom MyToolWindow extends the class SimpleToolWindowPanel.
In your custom myToolWindowFactory class, pass your custom MyToolWindow to ContentFactory.createContent() as the first argument. NOT one of the JPanel's inside MyToolWindow as is done in the ToolWindow examples given in the official IntelliJ documentation...
In your MyToolWindow constructor, call the method setContent(<YourJPanelContainingYourComponents>).
I found the answer by experimenting on example 5 from this link:
public JBTabbedTerminalWidget getTerminalWidget(ToolWindow window) {
window.show(null);
if (myTerminalWidget == null) {
JComponent parentPanel = window.getContentManager().getContents()[0].getComponent();
if (parentPanel instanceof SimpleToolWindowPanel) {
SimpleToolWindowPanel panel = (SimpleToolWindowPanel) parentPanel;
JPanel jPanel = (JPanel) panel.getComponents()[0];
myTerminalWidget = (JBTabbedTerminalWidget) jPanel.getComponents()[0];
} else {
NotificationUtils.infoNotification("Wait for Freeline to initialize");
}
}
return myTerminalWidget;
}

How to access delegate properties in ListView using index

I want to access delegate properties in ListView. I've tried with contentItem but sometimes it's undefined.
Here is my code:
ListModel{
id: modeldata
ListElement{
name:"don"
rank:1
}
ListElement{
name:"shan"
rank:2
}
ListElement{
name:"james"
rank:3
}
ListElement{
name:"jeggu"
rank:4
}
}
Component{
id: delegateitem
Row {
property int count: rank
Rectangle{
width: 100
height: 50
Text{
anchors.centerIn: parent
text: name
}
}
}
}
ListView{
id: listview
focus: true
anchors.fill: parent
model: modeldata
delegate: delegateitem
onCurrentIndexChanged: {
console.log("position",currentIndex)
console.log("property",contentItem.children[currentIndex].count);
}
}
Problem invalid output at position 1
qml: position 0
qml: property 1
qml: position 1
qml: property undefined
qml: position 2
qml: property 2
qml: position 3
qml: property 3
#Teimpz didn't really explain it well. Especially since there are bunch of qt project and ubuntu touch qml examples and use cases where you manage dynamically created list elements using javascript, and that is why they have javascript methods and properties
In QML there is more a notion of parent than a child, which is common in html. In bigger projects it is recommended (as you can also see in qt examples and in docs http://doc.qt.io/qt-5/qtqml-javascript-expressions.html#functions-in-imported-javascript-files) to have js logic separate from qml elements so you do access and manage elements from outside rather than pollute you qml elements with js logic, but not in a way of looking for children elements, but rather exposing children elements that you need.
In your case you should just use currentItem, same as you use currentIndex, so currentItem.count will give you what you need.
And if you don't need current item at all, you can access elements from model directly:
modelData.get(currentIndex).count, or listview.model.get(currentIndex).count
As for the hack that is mentioned by #Teimpz that is also one bad example. When you have more complex requirements and wanting specific elements inside delegate, every delegate has ListView.isCurrentItem property which you can attach and check. This would mean you can add property var myTargetItem to listview, and set it from child to whatever element you want if that delegate is current http://doc.qt.io/qt-5/qml-qtquick-listview.html#isCurrentItem-attached-prop
You can of course do that for any kind of event, maybe activeFocus so you could only reference activeFocused item.
This once again give you ability to expose only wanted elements without any advanced logic or lopping. Combining this with signals you can create very complex but clean interfaces without searching through children items.
So in the end maybe less nice but still better than searching for elements would be to add property int currentItemCount: 0 to listview. In delegate (Row element) you then add property bool isCurrentItem: ListView.isCurrentItem
so you get onIsCurrentItemChanged signal inside delegate, where you can do:
onIsCurrentItemChanged: if(isCurrentItem) listview.currentItemCount = count
so you have your current item count always set
The simple way is using itemAtIndex() like intemAt() in Repeater.
First of all: if you are trying to access list elements from outside your list, this is a good indicator that you should rethink your desing.
Now the solution: a listview has more children than only its items. You can filter them out by defining a property "property string type: "myType" " for example. Then find the items by looping over the children and only take those where the type property equals "myType".
Its somewhat of a hack but again you should really not be doing this in the first place.
myListView.itemAtIndex(currentIndex)).function_name()

Binding a ScrollViewer from ViewModel to View

I Build a scrollViewer and its elements in my ViewModel, and it's built into a property FrameworkElement PageElement I rebuild the pageElement every time some event happens, I want to bind the PageElement to a real scrollViewer in the View so that whenever I change pageElement, it draws itself in it's view.
Let me give you a little armchair advice. I don't know the details of your project but the details in your question make me draw a few conclusions.
First, to have your view model create UI elements is not wrong. But it is really unusual. It sounds like you might be missing the concept of data template or data template selector.
Using a data template allows you to have a rich presentation of data that is built as the individual record is generated and rendered in a repeater or in a single content control.
Using a data template selector allows you to have various different presentations of data that using code-behind logic will switch between based on data or other criteria.
Ref on templates: http://blog.jerrynixon.com/2012/08/windows-8-beauty-tip-using.html
Second, to have your UI be re-generated as the result of an event being raised sounds like a short path to performance problems.
Every time you manually create elements and add them to the visual tree, you put your app at risk of binding lag while the layout is re-rendered. Run your app on an ARM and I bet you may already see it. Then again, a simplistic UI may not suffer from this general rule of thumb.
Because I do not know the event, I cannot presume it is frequently occurring. However, if it is frequently occurring, then even a simplistic UI will suffer from this.
Now to answer your question
Sherif, there is no write-enabled property on a scrollviewer that will set the horizontal or vertical offset. The only way to set the offset of a scrollviewer is to call changeview().
var s = new ScrollViewer();
s.ChangeView(0, 100, 0);
You cannot bind to a method, so binding to something like this is a non-starter without some code-behind to read the desired offset and calling the method directly.
Something like this:
public sealed partial class MainPage : Page
{
MyViewModel _Vm = new MyViewModel();
ScrollViewer _S = new ScrollViewer();
public MainPage()
{
this.InitializeComponent();
this._Vm.PropertyChanged += (s, e) =>
{
if (e.PropertyName.Equals("Offset"))
_S.ChangeView(0, _Vm.Offset, 0);
};
}
}
public class MyViewModel : INotifyPropertyChanged
{
private int _Offset;
public int Offset
{
get { return _Offset; }
set
{
_Offset = value;
PropertyChanged(this, new PropertyChangedEventArgs("Offset"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
But let me caution you. The offset will need to be based on something. And those variables may change based on the window size, the font size, scaling from transforms, and lots of other factors. The code above will work most of the time, but it will possible fail frequently on other devices.
So, what to do? My recommendation is that you code this in your code-behind, monitoring for whatever scenario you feel would require a scroll, and simply programmatically scroll it from bode-behind. Beware, though, programmatically scrolling a scrollviewer could make your UI confusing to the user.
You know your app. You will have to choose.
Best of luck!

Blackberry Cascades Context Menu From Button Click

I'm using BlackBerry-10 Cascades to develop an app. I want a context menu to open on the right when I click a button. Currently I have it so that the menu opens after a press- hold of the button but I need it to open as soon as the button is tapped. I've tried finding a way to do this but cannot find it in the documentation. Is there any way I can invoke the context menu from the onclicked method of a button press?
BTW: this is all in QML
I am not really familiar with the controls available on blackberry-cascades, but, it seems like it should be as simple as moving the code from the onPressAndHold signal handler to the onClicked signal handler. For better help you should post the relevant snippets of your code along with the imports so we can find more info to help you in your particular scenario.
actions: [
//! [0]
ActionItem {
title: _webMaps.viewModeTitle
imageSource: "asset:///images/map.png"
ActionBar.placement: ActionBarPlacement.OnBar
onTriggered: {
_webMaps.nextViewMode()
map.setMapType(_webMaps.viewMode);
}
},
//! [0]
ActionItem {
title: qsTr("Waterloo")
imageSource: "asset:///images/pin.png"
ActionBar.placement: ActionBarPlacement.InOverflow
onTriggered: {
map.setCenter(43.468245, -80.519603);
}
}
]
try this sample..
In theory you should be able to do it but there are two problems that I see with this idea:
A context menu is supposed to be posted in the context of another UI element. I'm not sure what context menu items you might have for a button. If you are posting the context menu in the context of some other control then you will confuse your users.
The age old issue of non-conformance with the UI style guide of the platform. You will be expecting your uses, who have invested time in learning how to operate the BB10 UI, to now learn a different set of interface semantics.
There is a Context menu api in the BlackBerry Platform Services (BPS) library that you can use.
https://developer.blackberry.com/native/reference/core/com.qnx.doc.bps.lib_ref/topic/manual/dialog.h_functionscontextmenufunctions.html?f=dialog
It is not "Cascades functionality" per se, but you can use it from within a Cascades application. Note that it is a C based api so you would have to create some kind of "helper class" and expose it to QML yourself.
Try this sample code for open context menu on button clicked.

Dojo grid inside titlePane not getting painted until the browser is resized

I have an dojo enhanced grid inside a title pane which inturn in Tabcontainer. I am creating a tab container dynamically and painting the title pane which contains grid. For the first time the grid is painted properly but if i close the tab and again try it to open a tabcontainer title pane is painted but grid inside the titlepane is not painted (or rather its not visible) until i do a browser resize.
So anybody have faced similar kind of issue? Please let me know the solution for this.
I tried resize(), update() & startup() methods on grid nothing worked out.
I am kind of stuck please share your thoughts on this.
Thanks,
Vikram
I had the same problem and found a workaround by doing a dojo connect like:
dojo.connect(Datagrid,"_onFetchComplete",DataGrid,"_resize");
So it should automatically be resized, when DataGrid finished loading data.
Hope I could help.
Greeting, Simon
Have you tried setting an absolute height on the Grid?
Which browsers did you try? (I experienced various problems with DataGrid in TabCointainer using IE)
You must call the TabContainer.layout() each time its container is changing size. For doing this, you could 1) monitor DOMEvents onunderflow and onoverflow on containing DOMNode or 2) when container becomes visible (once-n-forall).
Reason why a window.onresize event fixes it is, that the TabContainer hooks on said event and calls its own layout.
In your situation, where the TabController fiddles with TabContainer's panes, there may be missing a 'layoutChildren' somewhere. Optimally, you should place the grid as the first on only child to tab.
After the grid is deployed, it will take an absolute, calculated height - 'inherited' from the TabContainer. This is fired once the TabContainer chooses to resize or instructed to do so.
Manually, you should be able to implement these lines - after re-opening a tab. The script is taken from _Grid.js to illustrate
var grid = dijit.byId('MYGRIDID');
require(["dijit/layout/utils"], function(layerUtils) {
layoutUtils.layoutChildren(grid.domNode,
grid._contentBox,
[grid.tablist, {
domNode: grid.tablistSpacer,
layoutAlign: titleAlign
}, {
domNode: grid.containerNode,
layoutAlign: "client"
}]);
grid._containerContentBox = layoutUtils.marginBox2contentBox(grid.containerNode,
{
domNode: grid.containerNode,
layoutAlign: "client"
});
// note this line in particular
grid.selectedChildWidget.resize(grid._containerContentBox);
}
My issue
I had a similar situation as yours:
Grid is in a titlepane (closed by default).
Grid can be destroyed and re-created on the fly.
Issue appears when user:
opens the pane.
closes the pane.
re-creates the grid.
re-opens the pane.
grid is not visible, until browser window is resized!
My solution
My approach was to force a resize() on my grid whenever the title pane was being opened.
I used code like this, in a place where I had access to both the grid and the panes:
var titlePane = registry.byId("title-pane-id");
var handle = aspect.after(titlePane, "toggle", function(deferred) {
if (titlePane.open) {
grid.resize();
}
});
The dojo/aspect doc
Don't forget to remove the aspect from your grid if you destroy it.
I did this on dojo v1.8.1
My solution is too easy: define on declaration of grid the bold parameter write here:
grid = new EnhancedGrid({id: 'MyIDgrid',
store: dataStore = new ObjectStore({objectStore: myStore}),
structure: structureGrid,
plugins: pluginGrid,
style : 'width: 725px; height: 350px',
autoWidth : true,
**autoHeight : false,height:'200px',**
elasticView : '2'
}, document.createElement('div'));
this resolve all!
Enjoy!
style="height: auto;" will fit the purpose.