How can I specify a line height in a multi-line Text / Label?
It already seems it's not supported in css:
JavaFX CSS does not support comma-separated series of font family
names in the -fx-font-family property. The optional line height
parameter when specifying fonts is not supported. There is no
equivalent for the font-variant property.
Is there a reason for this?
Is there an alternative to set this in code? Or kinda emulate the functionality? I want to control vertical rhythm within my app.
Java 8+ implementation
RT-21683 Allow to change line-to-line spacing was implemented to add a line spacing feature to JavaFX Java 8.
The line spacibg API is defined on Text, TextFlow and Labeled nodes via a lineSpacing property and associated getters and setters. Spacing is defined in pixels, like the rest of JavaFX layout.
Java 2.2- implementation
If you cannot use Java 8+, you will need to implement line spacing yourself, e.g. By setting spacing on a VBox with separate Text nodes for each line.
Discussion of unit measurements in JavaFX
It seems that there is a corresponding (stylable) css property? If it's defined in pixels, how do I make it dpi aware
css properties work can work with em units if you use them. See for example the default modena.css stylesheet which measures almost all sizes in em units. This is why if you are just using default styles without additional pixel based layout, then if you change the root font size, everything in your layout scales automatically.
It's only the Java APIs and FXML which work with only pixel units. Even then, the JavaFX system is clever enough to know (at least on some machines), that it is running on a HiDPI display (e.g. OS X retina) so that, for example, pixel units are automatically doubled for the retina display (in Java 8).
If you are using FXML, you could use expression bindings as a way to define your own unit system.
Future versions of JavaFX may provide more support for RT-14020 Concept for a screen resolution independent definition of a GUI.
Sample Code
The sample below uses the Java 8 to demonstrate usage of the lineSpacing api and -fx-line-spacing css attribute.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;
public class LineSpacing extends Application {
public static void main(String[] args) { launch(LineSpacing.class); }
#Override public void start(Stage stage) {
Label label = new Label(
"Power is my mistress.\n" +
"I have worked too hard at her conquest " +
"to allow anyone to take her away from me.\n" +
" - Napoleon Bonaparte");
label.setPadding(new Insets(20));
// as the default font spacing in JavaFX is 13 points,
// all of the following lines will provide the same results
// (double spacing the label text).
//
// label.setLineSpacing(13);
// label.setStyle("-fx-line-spacing: 13px;");
label.setStyle("-fx-line-spacing: 1em;");
stage.setScene(new Scene(label));
stage.show();
}
}
Related
I have two widgets
first, a QLineEdit that has a stylesheet (assume not accessible or changeable) that sets the color of the text to some color (e.g., red).
second, QLabel that has a default text color (e.g., black)
How can I get the color of the text in the QLineEdit, and set it as the color in the QLabel
l1 = QLineEdit()
color_to_be_set= l1.palette().highlight().color().name() # this approach is giving the wrong color
q1 = QLabel()
q1.setText("the text")
# then set the new color
"Trial#1 (Not working at all, not changing the color)"
palette = q1.palette()
palette.setColor(QPalette.WindowText,QColor(color_to_be_set))
q1.setPalette.....
"Trial#2, gives the wrong color"
q1.setStyleSheet("color:{}".format(color_to_be_set))
As already explained in comments, there's no public API to access properties set by stylesheets, and I sincerely doubt there could be any (at least, with the current implementation, including Qt6).
That said, there is some level of access to those properties, but it only works for the following:
background and background-color (which are the same);
color;
selection-background-color;
selection-color;
alternate-background-color;
Whenever any of the properties above is set, the palette of the widget is altered with the following palette color roles:
QSS property
QPalette role(s)
background, background-color
Base, Button, Window, backgroundRole()*
color
Text, ButtonText, WindowText, foregroundRole()*
selection-background-color
Highlight
selection-color
HighlightedText
alternate-background-color
AlternateBase
The * values are references to the widget class definitions (for instance, QPushButton normally uses Button as foreground role). They may be overridden by their respective setter functions.
Also, those properties only support the QSS Brush types: plain colors, gradients or palette(<role>). No pixmaps here!
Remember that in order to get proper access to those properties, the widget must be polished, which means that, if the widget has not been ever shown yet, calling ensurePolished() is mandatory:
Ensures that the widget and its children have been polished by QStyle (i.e., have a proper font and palette).
This means that any new Qt widget created without any already polished parent will use the system palette.
Consider the following:
from PyQt5.QtWidgets import *
app = QApplication([])
app.setStyleSheet('* {color: green;}')
source = QWidget()
# source.ensurePolished() # leave this commented for now
color = source.palette().windowText().color()
target = QWidget(styleSheet='background: {}'.format(color.name()))
target.show()
app.exec()
Then uncomment the fifth line, and see the result.
Now, there are a few catches.
When a stylesheet is set, any widget affected by it gets it's style() overridden by a private QStyleSheetStyle. That style is not exposed by the API and completely takes control over the style set for each affected widget.
But not always.
Take this snippet:
from PyQt5.QtWidgets import *
app = QApplication([])
app.setStyleSheet('* {background-color: black;}')
topLevel = QTextEdit()
topLevel.show()
parent = QWidget()
layout = QVBoxLayout(parent)
layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(QTextEdit())
parent.show()
app.exec()
With my current style (oxygen), the two backgrounds are slightly different:
On the left there's the topLevel widget, on the right the parent. As you can see, the underlying style still has some level of control.
This is an important aspect that has to be kept in mind, and trying to override it with a QProxyStyle won't be enough: the QStyleSheetStyle will "take control" and completely ignore the overridden functions, except for a very few like drawItemText(). Any other function will be called on "the C++ side" implementation, for which there's no useful override in the python bindings.
Also, whenever the underlying style is still used internally by the QSS, the result is that the color properties will be used just like any QPalette: they are reference colors, not the actual ones. Any widget will use the style implementation, which might use different color roles, or completely alter the color depending on the situation.
Whenever the default style is used (even through QStyleSheetStyle modifications), the palette is just a reference, not unlike a physical palette: give the same basic color palette to two styled painters, and they will use its colors in different ways. The actual color shown to the user might be slightly or even completely different and possibly ignored.
Concluding, the following aspects are of utmost importance:
just created widgets will use the default palette;
there is no access to border properties, nor margins and paddings (with the last two also depending on the inherited style);
top level widgets might behave differently;
the style might not completely adhere to the colors set (and even ignore them);
palette colors are references: the style might use a role for an unexpected purpose, or completely ignore it; some common styles use the Button role for item view headers, other use the Window role;
while normally respected, the above color properties might be altered by the style, just like any palette;
basic QSS color properties can be properly accessed only after the widget has been polished: ensurePolished() has to be called, which normally happens after the first visibility change and any setStyleSheet() call that affects the widget (see QStyle.unpolish() and QStyle.polish());
QPalette uses QBrush for any of its roles and groups, which could be any of the following: a QColor, a QGradient or a QPixmap; this means that if a palette role uses a gradient or pixmap, the returned color() will be invalid, etc.;
background-image does not update the palette (in fact, it's not listed in the table above); the same goes for border-image (as said above, there is no access to border properties);
the fusion style is (usually) the most reliable style to be used for extended stylesheet implementation; when unsure or getting unexpected behavior, use QApplication.setStyle('fusion') or widget.setStyle(QStyleFactory.create('fusion'));
OS styles potentially override drawing with private pixmaps in order to respect the standard OS appearance;
complex widgets require full and explicit values for their properties; never set generic properties (aka, no selectors) for parent widgets or the application[1], including QComboBox, QScrollBar and all QAbstractScrollArea subclasses (including item views);
So, considering all this, as long as a standard QStyle is used and the widget is polished, the roles explained in the above table should return the colors set in the supported properties of any stylesheet, including inherited ones.
But you can never be sure about that, and you always have to be aware of it.
[1] in the code examples above I did use generic stylesheets, but that was on purpose and for simplicity; that practice is usually highly discouraged for normal usage. Always use selectors when setting stylesheets for parents and complex widgets (see above).
How disable JavaFX Text
Text t = new Text();
t.setText("This is a text sample");
t.setDisable(true); //it does not work
Solution
You can style Text visually, so that it looks disabled, if you wish. The solution below binds the opacity setting to the disabled setting of the text.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class DisabledText extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
Text enabledText = new Text("Enabled");
Text disabledText = new Text("Disabled");
disabledText.opacityProperty().bind(
Bindings.when(
disabledText.disabledProperty())
.then(0.4)
.otherwise(1)
);
disabledText.setDisable(true);
VBox layout = new VBox(10, enabledText, disabledText);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
}
Notes
You might be able to accomplish this through CSS as well rather than a binding, I just didn't try that option.
In general, many times you are usually better off using a Label then Text for a lot of things as it includes more flexibility and functionality.
Background
Even though the user can't interact directly with text, so disabling text really has no effect, I guess what you are looking for is that the text look like other things when they are disabled.
Normally what happens when a control such as menu item or button is disabled, is that the CSS pseudo-class :disabled is set on the control. When this occurs, the default modena.css stylesheet for JavaFX 8 will modify the opacity for the control to -fx-opacity: 0.4;. This changes the visual look for the control so that it appears as though it is grayed out, as the low opacity makes the control looks faded.
Text is built to be a base drawing shape. Base drawing shapes in Java deliberately don't rely on CSS (though CSS can be used with them if needed - see the CSS reference guide for info on what default CSS rules apply to shapes). If your scene doesn't use any layout panes or controls, then the standard modena.css stylesheet will not be loaded and CSS processing will not be applied to the scene (for efficiency reasons so that, for instance, you could write a high performance game that did not use CSS processing). So CSS processing for Text is not necessarily required and, if used, is limited in its scope.
Two possible ways:
Add css style to the Text's instance.
Use Label instead.
I recommend using Label instead of Text.
I am confused with the possibilities of layouting components in Vaadin 7 - intuition tells to use just layout - however there are also Panels or Components which can be useful.
I need to create view which will have static left menu and top bar (constant size, visible on each page). The only thing that will change the content is middle component - which should be scrollable.
My design therefore is:
Custom component, which assembles everything. Is has absolute size for top and left menu.
Vertical layout for left menu and Horizontal layout for upper menu
Panel for middle component, since Panel is supposed to support scrolling.
Inside of panel I placed CssLayout with content that doesn't fit the page. Expected behaviour is that there would be a scroll bar (preferably on the browser, but i guess it'd be on the panel). But What actually happens is that just the part that fits the screen is visible and the rest is cut. CssLayout is sizeFull and panel default, but I tried also other combinations and nothing has helped.
How can I combine layouts to have middle panel scrollable (if needed) and top and left one with absolute size?
Thanks a lot,
In Vaadin 7 & 8, you generally use one of the Layout implementations to arrange widgets within a UI subclass. For example, VerticalLayout, HorizontalLayout, and GridLayout. For pixel-positioning, use AbsoluteLayout. Of course these can be nested one within another. But don't go crazy with the nesting as it may result in overly complex HTML/CSS at runtime.
The Panel class, as the doc says, is a special container. It contains only a single component, typically another container such as a Layout. The Panel presents itself with a border and a title bar displaying its caption property. This looks something like a sub-window.
As you mentioned, a Panel supports scrolling. But keep in mind that any web page defined with a Layout also supports scrolling, obviously.
Here is a complete example app showing a stack of TextField widgets in a VerticalLayout.
package com.basilbourque.example;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import javax.servlet.annotation.WebServlet;
import java.time.Instant;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {#link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
#Theme ( "mytheme" )
public class MyUI extends UI {
#Override
protected void init ( VaadinRequest vaadinRequest ) {
final VerticalLayout layout = new VerticalLayout();
for ( int i = 1 ; i <= 100 ; i++ ) {
TextField textField = new TextField( String.format( "%3d" , i ) );
textField.setWidth( 17 , Unit.EM ); // Widen the field to fully display our contents.
textField.setValue( Instant.now().toString() );
layout.addComponents( textField );
}
setContent( layout );
}
#WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
#VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet {
}
}
As to your idea of nesting VerticalLayout and HorizontalLayout for nav panels, button/tool bars, and such…
Vaadin 7 & 8: Absolutely proper, and a good idea. Sometimes a GridLayout makes sense for that as well.
Vaadin 10: May also be a good idea. Or you may want to explore using new CSS3 features flexbox and Grid Layout.
As for your problem with CssLayout inside a Panel, I have rarely used either. I suspect the problem would lie in not properly defining a specific width & height of the CssLayout. You did not post details and code, so we can only speculate.
I have to write an XNA game for a project in my college program. I'm making a turn-based RPG in the vein of the Gameboy Pokemon games. I'd like to align the menu options with a container at the bottom of the screen, with never more than 4 options. This should give you a rough idea about how I want the combat screen to look:
I currently have a menu system that aligns all the menu items with the center of the screen, meaning menus end up looking like this:
Start Game
Options
Quit
but in the center of the screen.
What is the best way to position the menu items in a similar fashion to the image above? Should I hardcode in the positions for the menu items, should I try and make some kind of class to act as the container for these items and have their position be relative to the size and position of the container, or use some other method?
If you don't want to hardcode the positions it could get pretty complex, depending on how much flexibility you want.
I would create a container class which is part of a tree structure, (see scene graph), with a parent container and a list of child containers. That is the most common way of handling relative positioning. Here a quick example:
public class Container
{
public Container Parent { get; set; }
public List<Container> Children { get; set; }
public Vector2 RelativePosition { get; set; }
public Vector2 AbsolutePosition
{
get
{
// The container is the root node
if (Parent == null)
return RelativePosition;
else
return RelativePosition + Parent.AbsolutePosition;
}
}
}
If you need even more flexibilty you could create a floated layout, where the elements are positioned dynamically depending on their size.
As stated by Lucius, creating a Container class is the best solution.
Currently I'm developing an UI application for the XBox.
Therefor I needed something like a positioning engine, with everything being relative, so I didn't need to calculate pixel stuff everytime.
What I did was creating a Container class, which contains roughly following attributes:
VectorTopLeft (Which the element which contains a Container object uses for drawing)
VectorTopRight
VectorBottomLeft
VectorBottomRight
Align (Enum: Right, Center, Left)
VerticalAlign (Enum: Top, Middle, Bottom)
NewRow (bool)
PreviousContainer (Container)
ParentContainer (Container)
Width (Getter)
Height (Getter)
PercentageHeight (getter/setter) (Percentage of the height of the parent container)
PercentageWidth (getter/setter) (Percentage of the width of the parent container)
PixelHeight (getter/setter) (Absolute height in pixels)
PixelWidth (getter/setter) (Absolute width in pixels)
AspectRatio: Used for setting the width to a ratio of the height, usefull for different screen aspects (4/3 or 16/9 for example)
MarginLeft
MarginRight
MarginTop
MarginBottom
The following vectors include margins, these are vectors used by the alignment procedure.
AbsoluteVectorTopLeft
AbsoluteVectorTopRight
AbsoluteVectorBottomLeft
AbsoluteVectorBottomRight
The following size attributes also include margins, usefull for calculating remaining sizes
AbsoluteWidth (getter)
AbsoluteHeight (getter)
And then some flags which get set to true if something crucial changes, and vectors/size stuff needs to be recalculated.
The alignment stuff is pretty complex, as in the fact that it uses recursion, and also calls previous container functions to shift everything to the right place.
The newrow attribute tells the system that it needs to start the element at a new row in the parent container, and is used for keeping the vertical alignment in mind.
The system might have some minor flaws, but at this moment it works as a charm for all my GUI related positioning stuff, and it works pretty damn fast!
Is it possible to customise the title bar in Java ME?
You might like to check out J2ME Polish, which provides a massive amount of UI customisation for MIDlets, including the title bar: link text
The API doesn't provide functionality for customising the default title bar, but we can attempt to write our own bar. This is in itself a minor breach of UI conventions. Some phones allow us to use setTitle(null) to remove the title. The phones in the Java mobile toolkit behave this way, but the Series 40 and 60 emulators don't seem to support this and instead generates a default title. On the other hand, the Sony Ericssons and Motorolas I've tested seem to support this.
However, we can detect whether the ability to remove the titlebar is present. We do not use the sizeChanged callback as the calling of this function may be delayed when the canvas is not visible. Instead we call getHeight both before and after removing the bar. According to the spec, getHeight should always return the correct and up to date value, even when the canvas is not being displayed. Here is code for implementing the detection:
public static boolean HIDE_TITLE_ENABLED;//Whether the implementation allows us to hide the title bar
static{
//See if we can remove the title by ensuring it is non-nil, then attempting
//to remove it. If we can't, then reset it.
Canvas c=new Canvas(){
protected void paint(Graphics g){
}
};
c.setTitle("test");
int preHeight=c.getHeight();
c.setTitle(null);
int afterHeight=c.getHeight();
HIDE_TITLE_ENABLED=preHeight!=afterHeight;
}
It is also possible to hide the title bar using full screen mode, but this hides other elements as well. This method is popular in games.