How to bind Node style (or styleClass) to a property? - css

Consider the following example:
class MainView : View("Example") {
val someBooleanProperty: SimpleBooleanProperty = SimpleBooleanProperty(true)
override val root = borderpane {
paddingAll = 20.0
center = button("Change bg color") {
action {
// let's assume that new someBooleanProperty value is updated
// from some API after button clicked
// so changing style of the borderpane in action block
// of the button is not the solution
someBooleanProperty.value = !someBooleanProperty.value
}
}
}
}
class Styles : Stylesheet() {
companion object {
val red by cssclass()
val green by cssclass()
}
init {
red { backgroundColor += Color.RED }
green { backgroundColor += Color.GREEN }
}
}
How can I dynamically change the background color of borderpane depending on someBooleanProperty (e.g RED when true and GREEN when false)? Is there possibility to bind CSS class to a property? Is there any solution to do that without using CSS (meaning inside style block etc)

If you want to toggle a class (add or remove a class based on a boolean property), you can use the Node.toggleClass(CssRule, ObservableValue<Boolean>) function.
val someBooleanProperty = SimpleBooleanProperty(true)
...
borderpane {
toggleClass(Styles.red, someBooleanProperty)
toggleClass(Styles.green, someBooleanProperty.not())
}
If, on the other hand, you want to bind to a changing class value, you can use the Node.bindClass(ObservableValue<CssRule>) function.
val someClassyProperty = SimpleObjectProperty(Styles.red)
...
borderpane {
bindClass(someClassyProperty)
}
You can then set the class to whatever you want.

Related

SetIcon change color

Hello i am trying to set icon of bottom nav bar
Icon
But everytime it changes color of whole icon and then it look like this
Icon
Is it possible to not give icon color so it wont change whole icon?
You could try to use the custom renderer to the set the icon. And set the ItemIconTintList to null.
[assembly: ExportRenderer(typeof(AppShell), typeof(ShellCustomRenderer))]
namespace App_Shell.Droid
{
class ShellCustomRenderer : ShellRenderer
{
public ShellCustomRenderer(Context context) : base(context)
{
}
protected override IShellBottomNavViewAppearanceTracker CreateBottomNavViewAppearanceTracker(ShellItem shellItem)
{
return new CustomBottomNavAppearance();
}
}
public class CustomBottomNavAppearance : IShellBottomNavViewAppearanceTracker
{
public void Dispose()
{
}
public void ResetAppearance(BottomNavigationView bottomView)
{
}
public void SetAppearance(BottomNavigationView bottomView, IShellAppearanceElement appearance)
{
bottomView.ItemIconTintList = null;
IMenu myMenu = bottomView.Menu;
IMenuItem myItemOne = myMenu.GetItem(0);
if (myItemOne.IsChecked)
{
myItemOne.SetIcon(Resource.Drawable.cactus_24px); // selected icon
}
else
{
myItemOne.SetIcon(Resource.Drawable.icon_about); //default icon
}
}
}
}
Update:
If you want to change the burger icon of shell, you could use Custom renderer to reset the icon vis CreateToolbarAppearanceTracker.
For more details, you could refer to the thread i done before. How to hide back button in navigation bar using Xamarin.Forms - AppShell?
If you want to change the back button color of navigation bar, you could set in style. Please check the link below. Change back button color in Xamarin.Android

Adding more items to page (pagination javafx)

i am using kotlin to create some small application with it, and tornadofx, however i am unfamiliar with how some of the javafx things translate to kotlin.
I am trying to itterate through a list of buttons, and add button to page, but i managed only to add one button per page, and i want to have X items per page, and i want page count to be dynamic
override val root = tabpane{
setPrefSize(800.0,600.0)
tabs.removeAll()
tabClosingPolicy = TabPane.TabClosingPolicy.ALL_TABS
val buttonList = arrayOf(
Button("Btn1"),
Button("Btn2"),
Button("Btn3"),
Button("Btn4")
)
tab("My Tab 1") {
hbox {
button("Click Me!") {
setOnAction {
replaceWith<TestView>()
}
}
button("Add Tab 2") {
setOnAction {
tab("tab2 ")
{
select()
button("Close") { setOnAction { close() } }
pagination(buttonList.size) { setPageFactory { pageIndex -> (buttonList[pageIndex]) } }
style {
arrowsVisible = false
pageInformationVisible = false
}
}
}
}
}
}
}
i tryed to implement a for loop that would add certain amount of items per page (lets say 2 buttons per page) and then proceed to create page. but i had no luck.
If anyone could help me find a proper solution for kotlin based pagination i would be thankful
pagination(buttonList.size)
with this line i can controll amount of pages, so that is at least something, but i dont see how to control amount items per page.
pageIndex -> (buttonList[pageIndex])
i realize issue is here, but i had no luck implementing proper logic.
lets say i want 4 items per page, i could get amount of pages required by dividing my list with number 4 (items per page). but not sure how to add more then 1 item per page.
class SomeView : View() {
private val buttonList = arrayOf(
Button("Btn1"),
Button("Btn2"),
Button("Btn3"),
Button("Btn4"),
Button("Btn5")
)
private var pageSize = 3 //Decide this however you want
override val root = tabpane {
setPrefSize(800.0, 600.0)
tabs.removeAll()
tabClosingPolicy = TabPane.TabClosingPolicy.ALL_TABS
tab("My Tab 1") {
hbox {
button("Click Me!") {
setOnAction {
replaceWith<TestView>()
}
}
button("Add Tab 2") {
setOnAction {
tab("tab2 ")
{
select()
button("Close") { setOnAction { close() } }
//The ceiling calc is for rounding up for the size
pagination(ceil(buttonList.size.toDouble() / pageSize).toInt())
{ setPageFactory { pageIndex -> createPage(pageIndex * pageSize, pageSize) } }
style {
arrowsVisible = false
pageInformationVisible = false
}
}
}
}
}
}
}
private fun createPage(startingIndex: Int, pageSize: Int = 1) : Pane {
val pagePane = VBox() //Create and design whatever pane you want
val endingIndex = minOf(startingIndex + pageSize, buttonList.size) //Don't go out of bounds
for(i in startingIndex until endingIndex) { //Assuming starting index is in bounds
pagePane.add(buttonList[i])
}
return pagePane
}
}
Here is a solution for dynamic pages for pagination. Your main problem was that tab panes only hold one child pane/component. The answer is to nest your buttons in a pane. I haven't tested this hard for edge cases or anything, so this is more proof-of-concept. You'll have to account for those if they come up.
Side Notes: As explained above, this is also why your "Close" button isn't appearing. Also, when I was testing your code I noticed the style wasn't implemented unless I clicked a button in the pane. Maybe it would work if you used a stylesheet, created a cssclass, and added that class to your tabpane (Don't forget to import your stylesheet if you choose to do this).

Button border-radius transition in JavaFX/TornadoFX

I am trying to make my buttons animate on hover such as those of Discord.
I can change the background color and border radius, but not able to animate it smoothly.
I could only find examples of animating shapes, and not css properties.
Here is my css code for the button.
class NavigatorButtonViewCss: Stylesheet() {
companion object {
val face by cssclass()
val buttonBackgroundColor = c("#36393F")
val buttonHoverBackgroundColor = c("#7289DA")
val textColor = c("#C8C9CB")
}
init {
indicator {
prefWidth = 10.px
}
face {
prefWidth = 50.px
prefHeight = 50.px
backgroundColor += buttonBackgroundColor
backgroundRadius = multi(box(50.percent))
label {
textFill = textColor
}
and(hover) {
// I want this to be animated
backgroundColor += buttonHoverBackgroundColor
backgroundRadius = multi(box(35.percent))
}
}
}
}
My button now
What I want
Are there any ways to achieve this transition?
Thank you.
JavaFX does not allow you to create any kind of animation via CSS and as far as I can tell there's no way of applying a animation even with TornadoFX. (Note tough that I just started with TornadoFX today, so I may be wrong.)
The only way of getting an animation in via stylesheet would be to use set the skin to a custom skin implementing the corner animation. While you could make the skin provide CSS property controling the roundedness of the corners.
Usually you'd extend Button adding a property, but in this case I simply store it in the properties map.
Skin to assign to the button
package org.example
import com.sun.javafx.scene.control.skin.ButtonSkin
import javafx.beans.binding.Bindings
import javafx.beans.binding.DoubleBinding
import javafx.beans.property.DoubleProperty
import javafx.beans.property.SimpleDoubleProperty
import java.util.function.Function
import javafx.css.SimpleStyleableBooleanProperty
import javafx.css.CssMetaData
import javafx.css.StyleablePropertyFactory
import javafx.css.Styleable
import javafx.css.StyleableProperty
import javafx.scene.control.Button
import javafx.scene.shape.ArcTo
import javafx.scene.shape.ClosePath
import javafx.scene.shape.HLineTo
import javafx.scene.shape.MoveTo
import javafx.scene.shape.Path
import javafx.scene.shape.VLineTo
import tornadofx.*
class AnimatedButtonSkin(button: Button) : ButtonSkin(button) {
companion object {
#JvmField
val CSS_ROUNDED_KEY = "org.example.AnimatedButtonSkin.rounded"
#JvmField
val CSS_ROUNDED_METADATA: CssMetaData<Button, Boolean>
#JvmField
val FACTORY = StyleablePropertyFactory<Button>(javafx.scene.control.SkinBase.getClassCssMetaData())
init {
CSS_ROUNDED_METADATA = FACTORY.createBooleanCssMetaData(
"-fx-rounded",
object : Function<Button, StyleableProperty<kotlin.Boolean>> {
override fun apply(b: Button): StyleableProperty<Boolean> {
// property stored in properties to avoid extending button
val v = b.getProperties().get(CSS_ROUNDED_KEY)
return v as StyleableProperty<Boolean>
}
},
true
)
}
}
override fun dispose() {
// get rid of the property and the shape
val b = getSkinnable()
b.getProperties().remove(CSS_ROUNDED_KEY)
b.setShape(null)
super.dispose()
}
private fun createArc(
cornerSizeH: DoubleBinding,
cornerSizeV: DoubleBinding,
invertX: Boolean,
invertY: Boolean
): ArcTo {
return ArcTo().apply {
setAbsolute(false)
setSweepFlag(true)
radiusXProperty().bind(cornerSizeH)
radiusYProperty().bind(cornerSizeV)
xProperty().bind(if (invertX) cornerSizeH.negate() else cornerSizeH)
yProperty().bind(if (invertY) cornerSizeV.negate() else cornerSizeV)
}
}
override fun getCssMetaData(): List<CssMetaData<out Styleable, *>>? {
return FACTORY.getCssMetaData()
}
init {
val prop = SimpleStyleableBooleanProperty(CSS_ROUNDED_METADATA, true)
button.getProperties().put(CSS_ROUNDED_KEY, prop)
// relative part of width/height that is rounded
// size for single corner:
// 0 -> rectangular button
// 0.5 -> circular button
val cornerSize = SimpleDoubleProperty(.5)
val w = button.widthProperty()
val h = button.heightProperty()
// bindings for horizontal measures
val cornerHSize = w.multiply(cornerSize)
val doubleHCornerSize = cornerHSize.multiply(2.0);
// measures for vertical measures
val cornerVSize = h.multiply(cornerSize)
val doubleVCornerSize = cornerVSize.multiply(2.0);
// lower part of the top-left corner
val start = MoveTo().apply {
yProperty().bind(cornerSize);
}
// straight path of top
val top = HLineTo().apply {
setAbsolute(false)
xProperty().bind(w.subtract(doubleHCornerSize))
}
// straight part of the right
var right = VLineTo().apply {
setAbsolute(false)
yProperty().bind(h.subtract(doubleVCornerSize))
}
// straight part of the bottom
val bottom = HLineTo().apply {
setAbsolute(false)
xProperty().bind(top.xProperty().negate())
}
// assemble the parts
val shape = Path(
start,
createArc(cornerHSize, cornerVSize, false, true), top,
createArc(cornerHSize, cornerVSize, false, false), right,
createArc(cornerHSize, cornerVSize, true, false), bottom,
createArc(cornerHSize, cornerVSize, true, true), ClosePath()
)
button.shape = shape
// animate open/close on change of stylable property
prop.addListener({ _, _, new -> cornerSize.animate(endValue = if (new) .5 else .2, duration = .2.seconds) })
}
}
Style
class NavigatorButtonViewCss: Stylesheet() {
companion object {
val face by cssclass()
val rounded by cssproperty<Boolean>("-fx-rounded")
val buttonBackgroundColor = c("#36393F")
val buttonHoverBackgroundColor = c("#7289DA")
val textColor = c("#C8C9CB")
}
init {
indicator {
prefWidth = 10.px
}
face {
prefWidth = 50.px
prefHeight = 50.px
backgroundColor += buttonBackgroundColor
textFill = textColor
skin = AnimatedButtonSkin::class
and(hover) {
rounded.value = false // update corners
backgroundColor += buttonHoverBackgroundColor
}
}
}
}

Hover effect on selected Text

I have a problem which I cannot solve with hover effect on Text.
Text t1 = new Text();
Text t2 = new Text();
Text t3 = new Text();
Text t4 = new Text();
When I select one Text I would like to change the background for example to gray. But the tricky part that I don't know how to solve is how I can remove the background color from the previous selected Text? It should work like Toggle button but with Text.
A Text object is a Shape, which defines a much more limited set of properties for styling than a control; for example it has no background. If you want to style the text in the UI, you should probably prefer a Label over Text. The other option would be to wrap the Text in some kind of Region (such as a StackPane) and apply the background to the region. This would complicate your code a little though.
Here are some solutions: the first uses no CSS and uses Labels, just setting their style as needed. The second uses a different approach in which you can use the "selection behavior" already defined in ToggleButton, but make the toggle buttons look like plain text. This is probably the best solution, in that it uses existing functionality and just changes the appearance using CSS. The third is a refinement of the first in which you can factor the style out into a CSS, but implement the selection yourself.
Solution 1: all Java:
Create a property to store the selected text:
ObjectProperty<Label> selectedText = new SimpleObjectProperty<>();
Register a listener with the property to reset the style of the previous selected text and set the style of the new selected text:
selectedText.addListener((obs, oldSelectedText, newSelectedText) -> {
if (oldSelectedText != null) {
oldSelectedText.setStyle("");
}
if (newSelectedText != null) {
newSelectedText.setStyle("-fx-background-color: lightgray;");
}
});
Register mouse listeners with each text to make them the selected text when they are clicked:
Label t1 = new Label("One");
Label t2 = new Label("Two");
Label t3 = new Label("Three");
Label t4 = new Label("Four");
Stream.of(t1, t2, t3, t4).forEach(t ->
t.setOnMouseClicked(event -> selectedText.set(t)));
Solution 2: Use toggle buttons styled to look like plain text:
This would actually be my preferred solution: use the selection behavior already defined for ToggleButton, and just use CSS to change the appearance of the toggle buttons.
ToggleButton t1 = new ToggleButton("One");
ToggleButton t2 = new ToggleButton("Two");
ToggleButton t3 = new ToggleButton("Three");
ToggleButton t4 = new ToggleButton("Four");
ToggleGroup tg = new ToggleGroup();
Stream.of(t1, t2, t3, t4).forEach(t -> t.setToggleGroup(tg));
With the following external style sheet:
.toggle-button, .toggle-button:selected , .toggle-button:hover,
.toggle-button:armed, .toggle-button:focused, .toggle-button:selected:focused {
-fx-color: -fx-base ;
-fx-background-color: transparent ;
-fx-background-insets: 0 ;
-fx-background-radius: 0 ;
-fx-padding: 0 ;
-fx-text-fill: -fx-text-background-color ;
}
.toggle-button:selected, .toggle-button:selected:focused {
-fx-background-color: lightgray ;
}
Solution 3: Java with css pseudoclasses:
Use the property for the selected text as in the first solution, but manipulate the style classes instead of the style directly:
ObjectProperty<Label> selectedText = new SimpleObjectProperty<>();
PseudoClass selected = PseudoClass.getPseudoClass("selected");
selectedText.addListener((obs, oldSelectedText, newSelectedText) -> {
if (oldSelectedText != null) {
oldSelectedText.pseudoClassStateChanged(selected, false);
}
if (newSelectedText != null) {
newSelectedText.pseudoClassStateChanged(selected, true);
}
});
Create the text object, set a style class on them, and register the mouse listener as before:
Label t1 = new Label("One");
Label t2 = new Label("Two");
Label t3 = new Label("Three");
Label t4 = new Label("Four");
Stream.of(t1, t2, t3, t4).forEach(t ->
t.setOnMouseClicked(event -> selectedText.set(t)));
Then use an external style sheet, and apply whatever style you need to the selected text:
.label:selected {
-fx-background-color: lightgray ;
}
With any of these solutions, you can edit the styles to be as you need.
First create a css class.
.text:hover {
-fx-fill: blue;
}
.text:pressed {
-fx-fill: blue;
}
.text-selected {
-fx-fill: blue;
}
Then create a global variable.
Text selectText;
Then add this:
scene.getStylesheets().add(Teste.class.getResource("text.css").toExternalForm();
t1.getStyleClass().add("text");
t2.getStyleClass().add("text");
t1.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
if (selectText != null) {
selectText.getStyleClass().remove("text-selected");
}
t1.getStyleClass().add("text-selected");
selectText = t1;
}
});
t2.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
if (selectText != null) {
selectText.getStyleClass().remove("text-selected");
}
t2.getStyleClass().add("text-selected");
selectText = t2;
}
});

Use AS3 to write IOS like slide up menu

In an AS3 mobile App, I would like to have a menu of buttons or icons that slides up from the bottom. Using a SlideViewTransition, I can get part of what I want.
var transition:SlideViewTransition = new SlideViewTransition();
transition.direction = ViewTransitionDirection.UP;
transition.mode = SlideViewTransitionMode.COVER;
view.navigator.pushView(ShareView, null, null, transition);
This works, but it does not do two things that I need to do.
1) I want the new transition to only go up 1/2 of the screen so that the top part of the screen displays the view underneath.
2) I want the new view that covers to be partially transparent. By setting the alpha of the incoming view's contentGroup background alpha, the new view is transparent as it comes in. But, once it covers the view underneath the view becomes opaque.
this.contentGroup.setStyle('backgroundAlpha', 0.5);
Does anyone have any ideas of how I would have a view slide up 1/2 way and be transparent? I have no idea where to start, view skinning?, or subclass transition?, or use something in flash namespace instead of a spark view.
I decided it would be simplest to just use the lower level ActionScript and do the animation myself using methods that are normally applied to a Sprite, but in this case use a VGroup so that I could add spark elements to it. The following is the base class I wrote.
public class SlideUpDialog extends VGroup {
private var pctHeight:Number;
private var stopHeight:Number;
private var vertIncrement:Number;
public function SlideUpDialog(pctHeight:Number) {
super();
this.pctHeight = pctHeight;
if (stage) {
addedToStageHandler();
} else {
addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
}
private function addedToStageHandler(event:Event=null) : void {
removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
graphics.beginFill(0xEEEEEE, 0.8);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight * pctHeight);
graphics.endFill();
var bevel:BevelFilter = new BevelFilter(4, -45);
this.filters = [ bevel ];
x = 0;
y = stage.stageHeight;
stopHeight = y * (1 - pctHeight);
vertIncrement = y / 2 / 24 / 4;
}
public function open() : void {
addEventListener(Event.ENTER_FRAME, openFrameHandler);
}
private function openFrameHandler(event:Event) : void {
if (y > stopHeight) {
y -= vertIncrement;
} else {
removeEventListener(Event.ENTER_FRAME, openFrameHandler);
}
}
public function close() : void {
... the reverse of open
}
}

Resources