How to style a custom TreeCell with CSS in ScalaFX? - css

I want to change the background colour of a custom TreeCell using CSS, but setting the style property on the tree cell doesn't work. I can style the tree with alternate yellow and grey cells with a CSS file that looks like this:
.tree-cell:disabled {
-fx-padding: 3 3 3 3;
-fx-background-color: white;
}
.tree-cell:selected {
-fx-background-color: blue;
}
.tree-cell:even {
-fx-background-color: yellow;
}
.tree-cell:odd {
-fx-background-color: grey;
}
.tree-cell:drag-over {
-fx-background-color: plum;
}
and change the fill style of the text with an event handler that looks like this:
onDragEntered = (event: DragEvent) => {
val db = event.getDragboard
if (db.hasContent(customFormat)) {
textFill = Color.DEEPSKYBLUE
style() = "tree-cell:drag-over"
}
event.consume()
}
but the style of the tree cells doesn't change.

I eventually found the answer to my own question. The CSS file now looks like this:
.tree-cell:disabled {
-fx-padding: 3 3 3 3;
-fx-background-color: white;
}
.tree-cell:selected {
-fx-background-color: blue;
}
.tree-cell:filled:even {
-fx-background-color: lightyellow;
}
.tree-cell:filled:odd {
-fx-background-color: lightsteelblue;
}
.tree-cell.drag-over:filled {
-fx-background-color: plum;
}
I now get a plum colour when dragging over a filled cell. Empty cells stay white.
In order to get here I needed to understand the rules of "CSS specificity", although it was eventually possible to simplify the finished CSS file to make each case exactly match one selector.
The ScalaFX code now looks like this:
import scala.collection.JavaConversions._
// Copy the list, so that it isn't modified in place.
var oldStyleClass: util.Collection[String] = styleClass.toList
onDragEntered = (event: DragEvent) => {
val db = event.getDragboard
if (db.hasContent(customFormat)) {
textFill = Color.DEEPSKYBLUE
// Remember the original style by taking a copy.
oldStyleClass = styleClass.toList
// Restyle filled cells with .tree-cell.dragover:filled
// for the duration of the drag
styleClass.add("drag-over")
}
event.consume()
}
onDragExited = (event: DragEvent) => {
val db = event.getDragboard
if (db.hasContent(customFormat)) {
textFill = Color.BLACK
}
// Restore the original style.
styleClass.setAll(oldStyleClass)
event.consume()
}
Somewhere along the way I lost the animation for a failed drop. Otherwise I'm happy (but somewhat lonely in ScalaFX-land.)

Related

CodeMirror Line-Break doesn't add line number - Angular

I'm using code mirror from ngx-codemirror. I want to split the line when it fits to the width of the parent. I have found some solutions to split the like using,
lineWrapping: true
and in styles
.CodeMirror-wrap pre {
word-break: break-word;
}
Using this I was able to split the line but I need to show the line number too.
The line number is not shown for the line that was just split.
This is the stackblitz link to my issue : code-mirror-line-break-issue
Screenshot :
Please help me with this.
This is not feasible using Code Mirror options, as this is something that is a bit counter intuitive that is rarely (ever?) wanted.
Like I said in my comment, say 2 persons discussing on a phone/web chat about a piece of code/json. They will not see the same thing when one mentions a line number to the other if they have different windows/screen sizes
Solution
As a hack, you can create your own elements representing line numbers and place them over the default line numbers.
Here is the stackblitz demo
Note: This a a very basic example. If you change code mirror settings (font size, gutters,...), you might need to tweak the css or do more calculation based on these settings.
component.html
<div class='codeMirrorContainer'>
<ngx-codemirror
#codeMirror
[options]="codeMirrorOptions"
[(ngModel)]="codeObj"
></ngx-codemirror>
<ul class='lineContainer' [style.top.px]="-topPosition">
<li [style.width.px]='lineWidth' *ngFor="let line of lines">{{line}}</li>
</ul>
</div>
component.css
li
{
height: 19px;
list-style: none;
}
.codeMirrorContainer
{
position:relative;
overflow: hidden;
}
.lineContainer
{
position:absolute;
top:0;
left:0;
margin: 0;
padding: 5px 0 0 0;
text-align: center;
}
::ng-deep .CodeMirror-linenumber
{
visibility: hidden; /* Hides default line numbers */
}
component.ts
export class AppComponent
{
#ViewChild('codeMirror') codeMirrorCmpt: CodemirrorComponent;
private lineHeight: number;
public lineWidth;
public topPosition: number;
public lines = [];
codeMirrorOptions: any = ....;
codeObj :any = ...;
constructor(private cdr: ChangeDetectorRef)
{
}
ngAfterViewInit()
{
this.codeMirrorCmpt.codeMirror.on('refresh', () => this.refreshLines());
this.codeMirrorCmpt.codeMirror.on('scroll', () => this.refreshLines());
setTimeout(() => this.refreshLines(), 500)
}
refreshLines()
{
let editor = this.codeMirrorCmpt.codeMirror;
let height = editor.doc.height;
this.lineHeight = editor.display.cachedTextHeight ? editor.display.cachedTextHeight : this.lineHeight;
if (!this.lineHeight)
{
return;
}
let nbLines = Math.round(height / this.lineHeight);
this.lines = Array(nbLines).fill(0).map((v, idx) => idx + 1);
this.lineWidth = editor.display.lineNumWidth;
this.topPosition = document.querySelector('.CodeMirror-scroll').scrollTop;
this.cdr.detectChanges();
}
}

JavaFX tableview with CSS scrolling issue

I am currently facing a problem with javafx table. I have a tableview that shows a list of subjects. The background color of each row depends if a subject is able to be enrolled or not. Subjects with green background can be enrolled and subjects with pink background cannot be enrolled. The problem occurs when scrolling the table.
TableView before scrolling
TableView after scrolling down and up
After scrolling, the background color of rows have changed and the subjects with green background might become pink and vice versa. This works perfectly without adding a css to the table.
Code I used to set the background color of rows
tblAvailableSubjects.setRowFactory((TableView<Subject> param) -> {
TableRow<Subject> row = new TableRow<>();
row.emptyProperty().addListener((obs, wasEmpty, isEmpty) -> {
if(isEmpty) {
row.setContextMenu(null);
row.setStyle("-fx-border-color: transparent");
} else {
Subject subject = row.getItem();
if(subject.getSubjectEvaluation().equals(SubjectEvaluation.COMPLETED)) {
row.setStyle("-fx-background: #B2EBF2");
} else if(subject.getSubjectEvaluation().equals(SubjectEvaluation.FAILED)) {
row.setStyle("-fx-background: #FF0000");
row.setContextMenu(tblAvailableContext);
} else if(subject.getSubjectEvaluation().equals(SubjectEvaluation.OKAY)) {
row.setStyle("-fx-background: #8BC34A");
row.setContextMenu(tblAvailableContext);
} else if(subject.getSubjectEvaluation().equals(SubjectEvaluation.ENROLLWITHCOREQ)) {
row.setStyle("-fx-background: #FFEB3B");
row.setContextMenu(tblAvailableContext);
} else if(subject.getSubjectEvaluation().equals(SubjectEvaluation.CANTENROLL)) {
row.setStyle("-fx-background: #FFCDD2");
}
}
});
return row;
});
CSS for table
.table-view {
/* Constants used throughout the tableview. */
-fx-table-header-border-color: transparent;
-fx-table-cell-border-color: -fx-box-border;
/* Horizontal Lines*/
-fx-background-color: transparent;
}
.table-view .filler, .table-view .column-header
{
-fx-size: 40;
-fx-border-style: null;
-fx-border-color: rgb(200.0, 200.0, 200.0);
-fx-border-width: 0 0 1 0;
-fx-background-color: transparent;
}
.table-view .show-hide-columns-button
{
-fx-background-color: transparent;
}
.table-view .column-header .label,
.table-view .column-drag-header .label
{
-fx-alignment: CENTER_LEFT;
}
.table-view .column-header-background
{
-fx-background-color: transparent;
}
.table-row-cell {
-fx-cell-size: 30px;
}
.table-cell {
-fx-border-color: transparent;
-fx-border-width: 1;
}
EDIT: The subject's SubjectEvaluation value doesn't change, it seems that it switches the context menu and background color between rows when scrolling.
I hope someone could help me with this. Thanks.

JavaFX how to hide empty column cell in tableView

I am using a tableView with UNCONSTRAINED_RESIZE_POLICY.
And I want set all empty column cell to white background.
This is the current view.
I am trying to search the empty node, but I can't find it even if I search all the nodes contained in tableView by following code (test function):
private void test() {
ArrayList<Node> nodes = getAllNodes(tableView);
nodes.forEach(node -> {
if(node instanceof TableCell) {
if(((TableCell) node).getText() == null || ((TableCell) node).getText().isEmpty()) {
System.out.println(true);
}
}
});
}
public static ArrayList<Node> getAllNodes(Parent root) {
ArrayList<Node> nodes = new ArrayList<Node>();
addAllDescendents(root, nodes);
return nodes;
}
private static void addAllDescendents(Parent parent, ArrayList<Node> nodes) {
for (Node node : parent.getChildrenUnmodifiable()) {
nodes.add(node);
if (node instanceof Parent)
addAllDescendents((Parent)node, nodes);
}
}
Use a CSS stylesheet to apply the styles to remove the background from TableRowCells and instead add the background to the TableCells:
/* overwrite default row style */
.table-row-cell {
-fx-background-color: transparent;
-fx-background-insets: 0;
}
/* apply row style to cells instead */
.table-row-cell .table-cell {
-fx-background-color: -fx-table-cell-border-color, -fx-background;
-fx-background-insets: 0, 0 0 1 0;
}
.table-row-cell:odd {
-fx-background: -fx-control-inner-background-alt;
}
/* fix markup for selected cells/cells in a selected row */
.table-row-cell:filled > .table-cell:selected,
.table-row-cell:filled:selected > .table-cell {
-fx-background: -fx-selection-bar-non-focused;
-fx-table-cell-border-color: derive(-fx-background, 20%);
}
.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell:filled:selected .table-cell,
.table-view:focused > .virtual-flow > .clipped-container > .sheet > .table-row-cell .table-cell:selected {
-fx-background: -fx-selection-bar;
}
scene.getStylesheets().add(getClass().getResource("style.css").toExternalForm());
Note: There are no TableCells outside of existing columns. The background is applied to the TableRowCells.
Also retrieving the cells from a virtualizing control is a bad idea:
The cells are created during the first layout pass. They may not be present at the time you run your code.
Interacting with the control (e.g. by resizing it, scrolling it, ect.) may result in creation of additional cells. Any modifications you've done to the cells you found before by traversing the scene are not applied to those new nodes automatically.
The easiest way is:
.table-row-cell:empty {
-fx-background-color: transparent;
}

Alternate between two different colors every 3 elements with only 1 selector

I have a list of tr elements and I want to add CSS on them with the following pattern :
red
red
red
black
black
black
red
red
red
black
etc.
How can I do this ? for now I've been using :
tr:nth-child(6n+1) { color: red; }
tr:nth-child(6n+2) { color: red; }
tr:nth-child(6n+3) { color: red; }
... but how can I do it with only 1 selector ?
EDIT : Here is a jsfiddle link https://jsfiddle.net/1s5s05vk/2/
I think it will work faster with using css.
.mytable tr:nth-child(6n+1), .mytable tr:nth-child(6n+2), .mytable tr:nth-child(6n+3) {
background-color: red;
}
But if you want, you could use javascript for this.
As I said before.. you could add a class like "red" to any element that has iterator%6 (iterator mod 6) lower than 4.
var rows = document.getElementsByTagName('tr');
for (var i = 0, len = rows.length; i < len; i++){
if(i%6 < 4) {
rows[i].classList.add("red");
}
}
try this. Maybe it can help you.
tr:nth-child(-n+3) {
background: red;
}
tr:nth-child(n+4) {
background: blue;
}
What you want to do cannot be done with a single selector using CSS alone.
However, you can shorten the definition so you don't repeat code:
tr:nth-child(6n+1),
tr:nth-child(6n+2),
tr:nth-child(6n+3) {
color: red;
}

Is it possible to style two children differently in a GtkButton depending on its state?

GTK3: I have two GtkLabel widgets in a GtkButton (via a HBox) like this:
[name_label (black) value_label (grey)] - button inactive (white background)
[name_label (white) value_label (yellow)] - button active (black background)
When the button is toggled I want the background to turn black and the two labels should change color accordingly.
Is it possible to do this using CSS only?
This is what I have tried:
from gi.repository import Gtk, Gdk
window = Gtk.Window()
button = Gtk.Button()
hbox = Gtk.HBox()
name = Gtk.Label('Name')
value = Gtk.Label('Value')
value.set_name('value')
hbox.set_spacing(10)
hbox.pack_start(name, expand=False, fill=True, padding=0)
hbox.pack_start(value, expand=False, fill=True, padding=0)
button.add(hbox)
window.add(button)
window.connect('destroy', Gtk.main_quit)
window.show_all()
screen = Gdk.Screen.get_default()
css_provider = Gtk.CssProvider()
css_provider.load_from_path('style.css')
context = Gtk.StyleContext()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
Gtk.main()
style.css:
.button {
background-color: white;
background-image: none;
}
.button #value {
color: grey;
}
.button:active {
background-color: black;
background-image: none;
color: white;
}
.button:active #value {
color: yellow;
}
The value label remains gray when the button is pressed, so the last section does not apply. Is this something that is expected?
Ok, so I can get this to work by dynamically adding a class to the value label e.g. like this but the original question remains: Can it be done using CSS only?
EDIT: In newer versions of GTK3, e.g. 3.18.9 (the one that is included in Ubuntu Xenial), the CSS-only solution works as expected!
I leave the old solution below for those who are stuck with an older GTK version.
from gi.repository import Gtk, Gdk
window = Gtk.Window()
button = Gtk.Button()
hbox = Gtk.HBox()
name = Gtk.Label('Name')
value = Gtk.Label('Value')
value.set_name('value')
hbox.set_spacing(10)
hbox.pack_start(name, expand=False, fill=True, padding=0)
hbox.pack_start(value, expand=False, fill=True, padding=0)
button.add(hbox)
window.add(button)
window.connect('destroy', Gtk.main_quit)
window.show_all()
screen = Gdk.Screen.get_default()
css_provider = Gtk.CssProvider()
css_provider.load_from_path('style.css')
context = Gtk.StyleContext()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
ctx = value.get_style_context()
def active(widget):
ctx.add_class('active_value')
def inactive(widget):
ctx.remove_class('active_value')
button.connect('pressed', active)
button.connect('released', inactive)
Gtk.main()
And the corresponding CSS:
.button {
background-color: white;
background-image: none;
}
.button #value {
color: gray;
}
.button #value.active_value { /* value label when the button is pressed */
color:yellow;
}
.button:active {
background-color: black;
background-image: none;
color: white;
}

Resources