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();
}
};
Related
I have charts with 15-17 lines and the cursor modifier cuts off the view at 6 on phones. It looks great on tablets, but on phones, as said, only shows six. Is there a way to decrease the text size in the modifier to fit more lines?
As for Android I would suggest to take a look on Custom Cursors example and use it as a base. In this example CustomXySeriesTooltip inherits from TextView which is used to render tooltip's text. So you can set font size for it like on regular TextView ( in ctor or internalUpdate override ):
public CustomXySeriesTooltip(Context context, XySeriesInfo seriesInfo) {
super(context, seriesInfo);
this.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
}
And the code for Xamarin.Android should be similar to Java one.
As for iOS - you can use dataStyle property provided by SCICursorModifierStyle which allows to specify font size:
SCITextFormattingStyle * textFormatting = [SCITextFormattingStyle new];
textFormatting.fontSize = 16;
textFormatting.fontName = #"Helvetica";
cursor.style.dataStyle = textFormatting;
Or you can create custom modifier like in this example.
My TableView is populated with data from a list of objects. The first column is a Boolean value.
Instead of displaying True or False in the cell, I would like to display an image if True and leave the cell empty if it's False.
This is how I populate the TableView:
colStarred.setCellValueFactory(new PropertyValueFactory<>("starred"));
colDate.setCellValueFactory(new PropertyValueFactory<>("date"));
colTime.setCellValueFactory(new PropertyValueFactory<>("time"));
I know that I need to use a custom TableCell and a CellValueFactory but I'm having a hard time wrapping my head around the documentation (I have not used Java factories in the past).
My research has lead to several answers regarding similar situations, but they all seem to deal with just displaying an image in the cell. I have been unable to find a solution for checking a boolean to determine whether an image should be displayed or not.
How do I check the starredProperty of my objects and show an image if it is True?
Thank you for all the help everyone has provided me in the past!
I'll assume the column to be a TableColumn<MyItemClass, Boolean>.
You simply create TableCells that adjust their look according to the item that gets passed to the updateItem method.
In this case we'll use a ImageView as graphic of the cell.
The following images are displayed depending on the item of the cell:
no image if the cell is empty or contains null
imageTrue if the item is true
imageFalse otherwise
You may of course use imageFalse = null for an empty cell when the item is false.
final Image imageTrue = ...
final Image imageFalse = ...
// set cellfactory
colStarred.setCellFactory(col -> new TableCell<MyItemClass, Boolean>() {
private final ImageView imageView = new ImageView();
{
// initialize ImageView + set as graphic
imageView.setFitWidth(20);
imageView.setFitHeight(20);
setGraphic(imageView);
}
#Override
protected void updateItem(Boolean item, boolean empty) {
if (empty || item == null) {
// no image for empty cells
imageView.setImage(null);
} else {
// set image for non-empty cell
imageView.setImage(item ? imageTrue : imageFalse);
}
}
});
What happens when the program is displayed is this:
The TableView creates cells needed to display the items using the cellfactories.
The TableView assigns the items to the cells. These items may be changed multiple times. Cells may also become empty after being filled. When this happens the updateItem methods of the TableCells are called.
I've a really simple UI with a single NSPopUpButton. Its selectedIndex is bound to an int value ViewController.self.my_selection. As soon as I change the selection from the UI (i.e. a select the third item of the NSPopUpButton) I see that my_selection value changes. So far so good, what I'm trying to obtain is the opposed direction though. I want to change the my_selection value programmatically and see the NSPopUpButton selecting the item a the index that I've defined in my_selection. I erroneously supposed that behaviour was the default behaviour for bindings...
This is what I'm obtaining now:
NSPoPUpButton ---> select item at index 2 ----> my_selection becomes equal to 2
This is what I want to achieve (keeping also the previous behaviour)
my_selection ---> set value to 3----> NSPoPUpButton selected index = 3
Without a bit more info (see my comment) it's hard to see exactly what you're doing wrong. Here's how I got it working: First, create a simple class...
// Create a simple class
class Beatle: NSObject {
convenience init(name: String) {
self.init()
self.name = name
}
dynamic var name: String?
}
Then, in the AppDelegate I created a four-item array called beatles:
dynamic var beatles: [Beatle]?
func applicationDidFinishLaunching(aNotification: NSNotification) {
beatles = [Beatle(name: "John"),
Beatle(name: "Paul"),
Beatle(name: "Ringo"),
Beatle(name: "George")]
}
In Interface Builder I set things up so that this array provides the pop-up with its content:
This class also has a selectedIndex property that is bound to the pop-up button's selectedIndex binding - this property provides read-write access to the pop-up button's selection:
// Set the pop-up selection by changing the value of this variable.
// (Make sure you mark it as dynamic.)
dynamic var selectedIndex: Int = 0
My simple Win32 DialogBox contains two static text controls (IDC_STATIC_TITLE and IDC_STATIC_SECONDARY), here's what it looks like in the resource editor:
At run time, the text first string is updated dynamically. Also, the font of the that text string is replaced such that it's bigger than the IDC_STATIC_SECONDARY string below it. The resulting text string might span a single line, two lines, or more.
I want the other static control holding the secondary text to be placed directly underneath the title string at run time. However, my resulting attempt to re-position this control in the WM_INITDIALOG callback isn't working very well. The second string is overlapping the first. I thought I could use DrawText with DT_CALCRECT to compute the height of the primary text string and then move the secondary text string based on the result. My code is coming up a bit short as seen here:
DrawText returns a RECT with coordinates {top=42 bottom=74 left=19 right=461} Subtracting bottom from top is "32". That seems a little short. I suspect I'm not invoking the API correctly and/or an issue with the different mappings between logical and pixel units.
Here's the relevant ATL code. The CMainWindow class just inherits from ATL's CDialogImpl class.
CMainWindow::CMainWindow():
_titleFont(NULL),
_secondaryFont(NULL)
{
LOGFONT logfont = {};
logfont.lfHeight = 30;
_titleFont = CreateFontIndirect(&logfont);
}
LRESULT CMainWindow::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CString strTitle;
RECT rectDrawText = {}, rectTitle={}, rectSecondary={};
CWindow wndTitle = GetDlgItem(IDC_STATIC_TITLE);
CWindow wndSecondary = GetDlgItem(IDC_STATIC_SECONDARY);
this->GetDlgItemText(IDC_STATIC_TITLE, strTitle);
wndTitle.SetFont(_titleFont); // font created with Create
wndTitle.GetWindowRect(&rectTitle);
wndSecondary.GetWindowRect(&rectSecondary);
ScreenToClient(&rectTitle);
ScreenToClient(&rectSecondary);
rectDrawText = rectTitle;
DrawText(wndTitle.GetDC(), strTitle, strTitle.GetLength(), &rectDrawText, DT_CALCRECT|DT_WORDBREAK); // compute the actual size of the text
UINT height = rectSecondary.bottom - rectSecondary.top; // get the original height of the secondary text control
rectSecondary.top = rectDrawText.bottom; // position it to be directly below the bottom of the title control
rectSecondary.bottom = rectSecondary.top + height; // add the height back
wndSecondary.MoveWindow(&rectSecondary);
return 0;
}
What am I doing wrong?
Despite what its name may make it sound like, wndTitle.GetDC() doesn't return some pointer/reference that's part of the CWindow and that's the same every call. Instead, it retrieves a brand new device context for the window each time. (It's basically a thin wrapper for the GetDC() Windows API call, right down to returning an HDC instead of the MFC equivalent.)
This device context, despite being associated with the window, is loaded with default parameters, including the default font (which IIRC is that old "System" font from the 16-bit days (most of this screenshot)).
So what you need to do is:
Call wndTitle.GetDC() to get the HDC.
Call SelectObject() to select the correct window font in (you can use WM_GETFONT to get this; not sure if MFC has a wrapper function for it), saving the return value, the previous font, for step 4
Call DrawText()
Call SelectObject() to select the previous font back in
Call wndTitle.ReleaseDC() to state that you are finished using the HDC
More details are on the MSDN page for CWindow::GetDC().
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.