I've used html+css quite a lot but I'm completely new to javafx+css. So this will be a newbie question but I can't find the answer anywhere.
I have a large GridPane full of Labels (besides others). I can set padding for all these Labels like:
GridPane.containerLevel02 Label {
-fx-padding: 0 0 0 5;
}
This works. I want some of the labels to be more indented so I create the "leftIndent" class for them:
GridPane.containerLevel02 Label.leftIndent {
-fx-padding: 0 0 0 20;
}
This works too.
BUT, I don't want to specify the three zeros there because if I ever decide to add top/bottom padding to Labels in general, it will not affect the "leftIndent" labels.
What I'm looking for is something like:
GridPane.containerLevel02 Label.leftIndent {
-fx-padding-left: 20;
}
... like in "normal" CSS or ...
GridPane.containerLevel02 Label.leftIndent {
-fx-padding: null null null 20;
}
...or anything that would let me specify left padding leaving the top, right and bottom padding untouched with whatever values they currently have.
Is that possible? THANKS!
Since JavaFX does not provide a -fx-padding-left or similar, I think your easiest workaround is to use variables. For instance:
.root {
LABEL_PADDING_TOP: 0;
LABEL_PADDING_RIGHT: 0;
LABEL_PADDING_BOTTOM: 0;
LABEL_PADDING_LEFT: 5;
}
GridPane.containerLevel02 Label {
-fx-padding: LABEL_PADDING_TOP LABEL_PADDING_RIGHT LABEL_PADDING_BOTTOM LABEL_PADDING_LEFT;
}
GridPane.containerLevel02 Label.leftIndent {
-fx-padding: LABEL_PADDING_TOP LABEL_PADDING_RIGHT LABEL_PADDING_BOTTOM 20;
}
It still requires you to override all values which can be unwanted in certain use cases, but at least you have to specify your defaults only at one place.
Related
I'm trying to create a game board ( similar to a chess board) for a game in react, and I would like to arrenge the cells (rectangular divs, that take they're value from a state array) in different shapes ( circle, triangle, oval etc) thus creating a board of that shape.
I only know how to arrange divs with CSS flex or grid and that is always rectangular formation (as far as I know)
How can I achieve this?
Any help will be appreciated, thanks!
To dynamically position elements, you need to arrange them in code. We can do that by creating the elements in code, then setting their position programmatically. I'm working off this jsfiddle
First, the HTML:
<div id="my_container" />
It couldn't get simpler. We just want somewhere we can put our elements. Now, for minimal styling, we've got:
#my_container {
position: relative;
width: 200px;
height: 200px;
background-color: powderblue
}
.cell {
background-color: red;
width: 5px;
height: 5px;
position: absolute;
}
I've added colors so that you can see the elements in the generated output, but they're not necessary. What is necessary is those position lines. Making the #container div relative lets it behave nicely with whatever it's placed in, and making each of the .cell divs absolute causes them to be placed on absolute coordinates relative to their parent, which means we can put them where we want. Ok, what's next?
The cells. I assume your cells already exist, but since I don't have them, I've created code to build these cells, shown below:
const container = document.getElementById("my_container");
const number_of_cells = 30;
const cells = [];
for (var i = 0; i < number_of_cells; i++)
{
const newDiv = document.createElement("div");
newDiv.classList.add("cell");
container.appendChild(newDiv)
cells.push(newDiv);
}
The goal here is to put the cells as children of #my_container and also create an array of all the cells. (I think maybe this step is redundant but I don't care...It makes the next step easier.)
function PositionCells(cells, x, y, radius)
{
const incr_angle = (2*Math.PI)/cells.length;
for (let i = 0; i < cells.length; i++) {
var new_x = x+radius * Math.cos(incr_angle * i);
var new_y = y+radius * Math.sin(incr_angle * i);
cells[i].style.left = new_x+'px';
cells[i].style.top = new_y+'px';
}
}
PositionCells(cells, 50,50,30);
Basically what I've done is written a function to position all the cells in an ordered, programmatic way. If you want other shapes, you can pretty easily do the math to come up with where to position them.
And that's it!
I use JavaFX2.0 to my java application, then I use .fxml file to build my UI, then I use css to decorate button or label.
Just like:
/* JavaFX CSS - Leave this comment until you have at least create one rule which uses -fx-Property */
.lebel{
-fx-alignment: center ;
-fx-pref-width:20% ;
-fx-pref-height: 180 ;
-fx-background-color: transparent ;
-fx-text-fill: white ;
-fx-background-size: stretch;
-fx-padding: 0 0;
-fx-font-size: 20sp;
}
Five labels are placed horizontally, but the property -fx-pref-width:20% doesn't work on the UI. I am trying to use -fx-pref-width:% to design UI for different sizes of stage.
-fx-pref-width is defined by Region. See this.
This is the abstract from it:
Property: -fx-min-width, -fx-pref-width, -fx-max-width
Values: < number >
Default: -1
Comments: Percentage values are not useful since the actual value would be computed from the width and/or height of the Region's parent before the parent is laid out.
So, percentages are not supported. You would need to do it using code via binding perhaps.
It is possible to style the QProgressBar using only QSS when the value is 16 example?
ui->progresso->setValue(16);
Using a QSS like this:
QProgressBar {
//Default QSS
...
}
QProgressBar:value(16) {
background-color: #fc0;
}
My goal is:
- When the QProgressBar is 0: It will use background-color: transparent
- When the QProgressBar is greater than 0: show a gray bar and the "chunk" will be blue
- When the QProgressBar is greater than 89: shows the "chunk" in red.
I can do this with QT + C++, but would like to know is it is possible to do this only with QSS?
Like this (this code does not exist, is just one example):
QProgressBar {
background-color: gray;
}
QProgressBar:value(0) {
background-color: transparent;
}
QProgressBar::chunk {
background-color: blue;
}
QProgressBar::chunk:minValue(90) {
background-color: red;
}
I think it is possible with help of Property Selector but only for exect values i.e.:
QProgressBar[value = 16]::chunk{
background-color: red;
}
but you can generate such stilesheet in code for each value
QString styleSheet;
for(int i = 0; i < 101; i++)
{
styleSheet.append(QString("QProgressBar[value = \"%1\"]::chunk{background-color: %2;}").arg(QString::number(i), (i < 17 ? "red" :"blue")));
}
myProgressBar->setStyleSheet(styleSheet);
I don't try it. It's just a theory based on documentation.
Update 1
Warning: If the value of the Qt property changes after the style sheet has been set, it might be necessary to force a style sheet recomputation. One way to achieve this is to unset the style sheet and set it again.
This is not possible.
The only valid extensions are in the documentation and too long to post here.
However you could handle the valueChanged( int ) signal of the QProgressBar
and set the stylesheet accordingly using setStyleSheet( ) but I figure you already know that.
I've added a left and right padding to the header of the QTreeView using this QSS code:
QHeaderView::section{padding:7px 15px}
But the content of the columns is not aligned anymore with the headers.
How can I add a padding of 15px ( like in the header ) to the columns' content?
Edit: For some reasons I use delegates to draw the content of the QTreeView, that's why styling the QTreeView::item doesn't work ( like #svlasov suggested ).
painter.translate(15, 0) seems to fix this issue, but a weird effect appears when I select a row: the selection is not continuous.
Something like this:
QTreeView::item { border: 0px; padding: 0 15px; }
This is rude and crude, but it does what you're asking for in a real simple way for a 3 column QTreeWidget.
// Okay, I want to make sure my columns are wide enough for the contents, but I also
// Don't want them squished together. So use resizeColumnToContents, to make them
// as small as they can be to show the contents, then take those widths and add some
// spacing and then set the columns to the new widths.
m_tree->resizeColumnToContents (0);
m_tree->resizeColumnToContents (1);
int w0 = m_tree->columnWidth (0) + 20;
int w1 = m_tree->columnWidth (1) + 20;
m_tree->setColumnWidth (0, w0);
m_tree->setColumnWidth (1, w1);
I'm trying to emulate the CTRL+F functionality from Chrome that highlights matches on the page in the scrollbar, but for certain fields in a form. Using page offsets and percentages, I have blocks of color which correspond to the relative locations of those fields on the page.
In my prototype, the blocks of color sit to the left of the scrollbar. Ideally, they'd sit UNDERNEATH the scrollbar, and the scrollbar's track would be transparent so that it looks like they're part of the scrollbar track.
Can the default scrollbar be set to allow for overflow content to show underneath it (or allow page content to go over it)? I know this could be accomplished if I just rolled my own scroll, but I'd like to use the default ones provided by the browser if at all possible.
It's clearest if you just look at this Prototype.
CSS:
::-webkit-scrollbar {
width: 14px;
height: 18px;
background-color:transparent;
}
::-webkit-scrollbar-track,
::-webkit-scrollbar-track-piece {
background:none;
}
::-webkit-scrollbar-thumb {
height: 6px;
border: 4px solid rgba(0, 0, 0, 0);
background-clip: padding-box;
-webkit-border-radius: 7px;
background-color: #333
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-corner {
background-color: transparent;
}
I thought of rendering the matches on the trackbar similarly to what browsers do today before. The idea is simple by using linear-gradient background for the ::-webkit-scrollbar-track. However I did not try implementing this. Right after reading your question, I've tried it and looks like it's not such easy.
You can use the linear-gradient background OK, but if you try rendering more than 1 match (a line), it sometimes can't be rendered (especially when the window's size is changed) and the line is not rendered smoothly. Such as this seems to be OK:
//render 2 lines, one is at 50px and the other is at 100px
background: linear-gradient(transparent 50px, red 50px, red 51px, transparent 51px,
transparent 100px, red 100px, red 101px, transparent 101px);
but it's not stable, as I said when you try resizing the window, at some size, some line won't be rendered (at least I tried on Opera). When the window's height is large, the line even becomes blurred (not sharp) and thicker. I don't really understand this, because the color stops are set fixedly (by px, not by %). This issue is even worse when the number of lines is larger. You have a linear-gradient with many corresponding color stops. That seems to be a neat way to solve the problem. Just because of the undesired issue, we can't use that approach.
The new approach: So I tried using multi-backgrounds feature instead. Each background just renders 1 line, the background-size is the same for all the background is just about 2px height and the background-position should be different. Here is the equivalent code (to the above clean code) using this approach:
background: linear-gradient(red, red), linear-gradient(red, red);
background-repeat: no-repeat;
background-size: 100% 2px;
background-position: 0 50px, 0 100px;
The new approach of course requires that the browser has to support multi-backgrounds features (looks like just IE8- do not support this cool feature).
So that's almost what you need to solve this problem. Now we need to find how to apply that style using script. We can't select a pseudo-element (or something like that) via script. We can just use the window.getComputedStyle() method to get the read-only style of a pseudo-element. However we always have a way to modify the CSS directly. That's is by using pure JS with the help of document.styleSheets and cssRules. They allow us to insert/remove/modify a rule.
That looks great. But there is still another issue. When changing the style using that method, the style is not applied right (at least it happens to the ::-webkit-scrollbar-track, it may not happen to other elements). Only when you move the mouse over the scrollbar, the new style is applied. I've just found a simple way to invalidate that scrollbar by setting the overflow of document.documentElement (the html) to hidden and set it back to auto. That works almost well.
Here is the code:
var requiredTb = $(".required input");
var invalids = requiredTb;
var offsets = [];
//init offsets to highlight on the trackbar later
requiredTb.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
//find the rule styling the -webkit-scrollbar-track
//we added in the CSS stylesheet, this is done just 1 time
var sheets = document.styleSheets;
var trackRule;
for(var i = 0; i < sheets.length; i++){
var rules = sheets[i].cssRules || sheets[i].rules;
for(var j = 0; j < rules.length; j++){
var rule = rules[j];
if(rule.selectorText == "::-webkit-scrollbar-track:vertical"){
trackRule = rule; break;
}
}
}
//define an invalidate() method, we need to use this method
//to refresh the scrollbars, otherwise the newly applied style is not affected
window.invalidate = function(){
$(document.documentElement).css('overflow','hidden');
setTimeout(function(e){
$(document.documentElement).css('overflow','auto');
},1);
};
//this is the main function to set style for the scrollbar track.
function setTrackHighlights(positions, color){
positions.sort();//ensure that the input array should be ascendingly sorted.
trackRule.style.cssText = "";
var gradient = "background: ", backPos = "background-position: ";
var winHeight = $(window).height();
$.each(positions, function(i,pos){
gradient += "linear-gradient(" + color + ", " + color + "),";
backPos += "0 " + pos + "%,"
});
gradient = gradient.substr(0,gradient.length-1) + ";";
backPos = backPos.substr(0,backPos.length -1) + ";";
trackRule.style.cssText += gradient + backPos + "background-repeat:no-repeat; background-size:100% 2px";
invalidate();
}
//initially set the highlights on the trackbar
setTrackHighlights(offsets,'red');
//handle the oninput event to update the highlights accordingly
requiredTb.on('input', function(e){
var required = $(this).closest('.required');
var refreshHighlights = false;
if(this.value && !required.is('.ok')) {
required.addClass('ok');
refreshHighlights = true;
invalids = invalids.not(this);
}
if(!this.value && required.is('.ok')) {
required.removeClass('ok');
refreshHighlights = true;
invalids = invalids.add(this);
}
if(refreshHighlights){
offsets.splice(0);
invalids.each(function() {
offsets.push(($(this).offset().top)/document.body.scrollHeight * 100);
});
setTrackHighlights(offsets,'red');
}
});
You have to add an empty ::-webkit-scrollbar-track:vertical rule (we need to deal only with the vertical scrollbar) in the CSS code, it should be appended at the last to override any similar rule before. We can in fact use the insertRule() method (of a CSSRuleList which can be accessed via cssRules property) to add a new rule instead of looping through the styleSheets, and through the cssRules (of each sheet) to find the empty rule ::-webkit-scrollbar-track:vertical.
The code I posted here can be improved, such as you can add another method setTrackHighlights to allow to add more lines (instead of rendering all the lines each time we need to add/remove just 1 line)...
Note that by using the term line, I mean the rendering representation of a match on the trackbar.
Demo