I have figured out a way to change the style of tabs at run time with following logic:
var cssStyle:CSSStyleDeclaration = StyleManager.getStyleDeclaration(".MyTabs");
cssStyle.setStyle("borderColor", "red");
But here ".MyTabs" class is applicable to all the tabs between first and last tab. As per getStyleDeclaration javadoc, it only accepts "class selector" and "type selector" not the id selector.
How can I change the individual tab style at run time?
(tabNavigator.getTabAt(index) as Button).setStyle("borderColor", 0xFF0000);
This will solve the issue, you can set your own color as value param.
Another user pointed out a method that I had somehow missed, allowing you to access a Tab as a Buttom and style it from there.
var t:Button = theTabs.getTabAt(index);
Tab extends Button, so there may be some things you would need the below solution for, but for basic styling this should be enough.
#Sebastian's answer works for a TabBar, which I know you don't have, as this is the third identical question you've asked. In order to style the tabs on a TabNavigator, you need to access the internal TabBar.
//this import may not auto-complete for you
import mx.controls.tabBarClasses.Tab;
var t:Tab = theTabs.mx_internal::getTabBar().getChildAt(index);
Now you can feel free to set the styles, as shown in Sebastian's answer.
You can call setStyle on the individual Tab's, which u can get by calling TabBar.getChildAt(x) . Check the following link, which illustrates how to achieve the task you are trying to perform. You can also check out this link
private function tabBar_creationComplete():void {
var colorArr:Array = ["red", "haloOrange", "yellow", "haloGreen", "haloBlue"];
var color:String;
var tab:Tab;
var idx:uint;
var len:uint = tabBar.dataProvider.length;
for (idx = 0; idx < len; idx++) {
var i:int = idx % colorArr.length;
color = colorArr[i];
tab = Tab(tabBar.getChildAt(idx));
tab.setStyle("fillColors", [color, "white"]);
tab.setStyle("fillAlphas", [1.0, 1.0]);
tab.setStyle("backgroundColor", color);
}
}
Related
I have two css classes on a tornadofx label bound to a SimpleBooleanProperty. One which has a background image and a blue border and one which has no background image and a yellow border.
Snippet from View containing label:
val switch: SimpleBooleanProperty = SimpleBooleanProperty(false)
label("my label"){
toggleClass(UIAppStyle.style1, switch.not())
toggleClass(UIAppStyle.style2, switch)
}
Snippet from UIAppStyle:
s(style1){
textFill = Color.YELLOW
maxWidth = infinity
maxHeight = infinity
alignment = Pos.CENTER
backgroundImage += this::class.java.classLoader.getResource("img.png")!!.toURI()
backgroundPosition += BackgroundPosition.CENTER
backgroundRepeat += Pair(BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT)
borderColor += box(Color.BLUE)
}
s(style2){
textFill = Color.YELLOW
maxWidth = infinity
maxHeight = infinity
alignment = Pos.CENTER
borderColor += box(Color.YELLOW)
}
When switch = false, there is a background image and a blue border. When switch = true, there is the same background image and a yellow border. I'm not finding out how to get the background image to remove. Interestingly enough, if I add a different background image to style2, it changes correctly.
Edit: To remove two toggleClasses and introduce new strange problem:
class MyView : View(){
...
init{
...
row{
repeat(myviewmodel.numSwitches){
val switch = myviewmodel.switches[it]
val notSwitch = switch.not()
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, notSwitch)
}
}
}
}
This code snippet does not work for me. However, if I add private var throwsArray = mutableListOf<ObservableValue<Boolean>>() as a field of MyView and add notSwitch to the array, then the same exact code works. It's almost as if notSwitch is going out of scope and becoming invalidated unless I add it to a local array in the class?
I don’t understand why you want to have two different toggleClass for the same control. As you pointed out, the problem in your case is that when the backgroundImage is set, you need to set a new one in order to change it. But in your case, you only have to add the style without backgroundImage first and them set toggleClass with the style with backgroundImage. Like this:
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, switch)
}
button {
action {
switch.value = !switch.value;
}
}
Edit: This ilustrate what I'm talking about in comments:
class Example : View("Example") {
override val root = vbox {
val switch = SimpleBooleanProperty(false)
val notSwitch = switch.not()
label("my label"){
addClass(UIAppStyle.style2)
toggleClass(UIAppStyle.style1, notSwitch)
}
button("One") {
action {
switch.value = !switch.value;
}
}
button("Two") {
action {
notSwitch.get()
}
}
}
}
You can put the notSwitch.get() in any action and without trigger that action it does the work. Check how I put it in the action of button Two, but without clicking that button even once, it works.
This is actually some kind of hack, in order to achieve what you want. But I don’t see the reason why my initial solution with true as default value for property shouldn’t work.
Edited to do inverse of status
Here is simple example of a working toggle class using your styling:
class TestView : View() {
override val root = vbox {
val status = SimpleBooleanProperty(false)
label("This is a label") {
addClass(UIAppStyle.base_cell)
val notStatus = SimpleBooleanProperty(!status.value)
status.onChange { notStatus.value = !it } // More consistent than a not() binding for some reason
toggleClass(UIAppStyle.smiling_cell, notStatus)
}
button("Toggle").action { status.value = !status.value }
}
init {
importStylesheet<UIAppStyle>()
}
}
As you can see, the base class is added as the default, while styling with the image is in the toggle class (no not() binding). Like mentioned in other comments, the toggleClass is picky, additive in nature, and quiet in failure so it can sometimes be confusing.
FYI I got to this only by going through your github code and I can say with confidence that the not() binding is what screwed you in regards to the toggleClass behaviour. Everything else causing an error is related to other problems with the code. Feel free to ask in the comments or post another question.
I am trying to change the colour of my data points with bokeh. When I use a Hover tool this works fine. However, if I use the same callback function with a select or button tool it does not work. I guess this is beacause the change.emit() does not work in combination with a button or select?
How can I make my customJS work with a select or button tool?
callback3=CustomJS(args=dict(source2=source2,p2=p2),code=''' var source2=source2 var data3 = source2.data;
var color = data3['color'];
var i, n = color.length;
for (i = 0; i < n; ++i) {
color[i] = 'blue';
source2.change.emit();
}
''' )
For my the hoover tool I use:
plot.add_tools(HoverTool(tooltips=None, callback=callback3, renderers=[d],mode='vline'))
For the button:
button = Button(label="Foo", button_type="success")
button.js_on_click(callback3)
When I use an alert in my callback this works also for the button and the select.
I solved the problem. It was not related to the change.emit(). The problem was that I used show separately for the plot and the button.
I have written some code to hide specific markers in our maps based on checkboxes outside of the map itself. However, these markers also have vector features too (really on separate layers) but I want to just hide the features rather than destroy them. I tried using display(false) but get errors. Is there a function for hiding vectors?
Solution
Change the style property for OpenLayers.Feature.Vector instances. Set the display attribute to none or the visibility attribute to hidden. Redraw the layer afterwards.
According to comments in OpenLayers code:
display - {String} Symbolizers will have no effect if display is set to "none". All other values have no effect.
Example Code
For a given OpenLayers layer variable called layer, you could hide all the features as follows:
var features = layer.features;
for( var i = 0; i < features.length; i++ ) {
features[i].style = { visibility: 'hidden' };
}
layer.redraw();
This iterates over all features in a layer, allowing full control of the specific features to hide.
I have wrestled with OpenLayers a few times trying to get my features within the same layer to display exactly as I want. #igorti's solution overrides all of the feature's style properties, so I don't recommend this approach unless you have no reason to re-display the feature later on (in which case the removeFeatures() method is probably a better way to do this anyways).
Hiding Vector Features
The way I do this is to manually set the feature's style display to none and then redraw the layer. If I need to display the feature again, set the display property to block. Pretty simple:
function hideFeatures() {
var features = layer.features;
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (!isVisible(feature)) {
feature.style.display = 'none';
}
}
layer.redraw();
}
Re-Displaying Vector Features
Re-displaying hidden features is a little bit trickier depending on your situation. Take a look at the OpenLayers documentation on styling for some possibilities. But in general, if I need to display the feature again, I set the feature's style attribute to null. This ensures that when the OpenLayers renderer performs the drawFeature function, your pre-configured styles from your layer's styleMap are redrawn:
// from OpenLayers drawFeature()
if (!style) {
style = this.styleMap.createSymbolizer(feature, renderIntent);
}
So your display function might look something like this:
function displayFeatures() {
var features = layer.features;
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (isVisible(feature)) {
feature.style = null; //redraw the feature
}
}
layer.redraw();
}
Other Approaches
There are a couple other approaches to doing this. You can set the feature's fillOpacity and strokeOpacity to 0, like so:
function displayFeatures() {
var features = layer.features;
for (var i = 0; i < features.length; i++) {
var feature = features[i];
if (isVisible(feature)) {
feature.style.fillOpacity = 1;
feature.style.strokeOpacity = 1;
}
else {
feature.style.fillOpacity = 0;
feature.style.strokeOpacity = 0;
}
}
layer.redraw();
}
The downside to this approach is that any active map controls will still be able to interact with the "hidden" feature, so if a user accidentally clicks or hovers over the feature these events will still fire.
You can also create a style within your layer's styleMap called hidden, with either of the two approaches above. Then to hide a feature, simply change the feature's renderIntent to hidden.
Finally, you can add subsets of your features to separate layers, and call the layer's setVisibility method to false. This is only a good option if you don't have a need to interact with all features concurrently, since only controls for the top layer of your map will be active. (There are ways to use controls for multiple layers, but there's a lot more juggling involved and I don't recommend it unless it is absolutely necessary)
You can set display:'none' in style property. So that features will not be display
To hide features
for( var i = 0; i < features.length; i++ ) {
features[i].style = { display: 'none' };
}
layer.redraw();
To display back the hidden features
for( var i = 0; i < features.length; i++ ) {
features[i].style = null;
}
layer.redraw();
To hide the one feature
var feature = vectorlayer.getFeatureByFid(fid);
feature.style = { display: 'none' };
vectorLayer.removeFeatures(feature);
vectorLayer.addFeatures(feature);
It was not clear from your question whether you've already tried it, but if you haven't you might try the setVisibility() method.
Reference: http://dev.openlayers.org/releases/OpenLayers-2.10/doc/apidocs/files/OpenLayers/Layer-js.html#OpenLayers.Layer.setVisibility
Here is what I finally done for this matter as I had the same need but didn't want to hide each feature individually or play with CSS style:
I'll assume that you have something like the following somewhere:
myVector = new OpenLayers.Layer.Vector(...
Then link this code to the button or whatever event you need:
if( myVector.getVisibility() && myVector.features.length > 0 ) {
myVector.setVisibility(false);
} else {
myVector.setVisibility(true);
}
getVisibility() / setVisibility() references are missing from vector part but are in layer documentation.
I'm making a trivia in an Air application, a question, three buttons, after you choose one, the right button gets coloured green, the wrong ones get coloured red. I was trying to do this changing the styles, so I created a Button.Right and a Button.Wrong style, but I also need to disable the buttons so they don't get clicked more than once while I'm showing the correct answers.
So I'm having trouble making it so the buttons don't look greyish and with the alpha turned down when I set their enabled property to false.
I'm trying to be as minimalistic as possible here, changing disabled-overlay-alpha or disabledOverlayAlpha in the css file doesn't seem to do the trick, neither does changing disabledBorderColor
any fast tricks to do this?
This might be a somewhat dirty workaround, but you could try disabling the buttons by setting their mouseEnabled property to false to forbid them from interacting with the mouse.
I have always used stateful skins to accomplish this. It's relatively easy, but this is how I would do it:
CSS (path changes depending on your assets):
Button {
skin:ClassReference('com.mysite.assets.skins.MyStatefulSkin');
}
Then, in /com/mysite/assets/skins, you would have:
package com.mysite.assets.skins {
import flash.display.GradientType;
import mx.containers.Canvas;
public class MyStatefulSkin extends Canvas {
import flash.display.Graphics;
import flash.geom.Rectangle;
import mx.graphics.GradientEntry;
import mx.graphics.LinearGradient;
public function MyStatefulSkin() {
super();
}
protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth,unscaledHeight);
var w:Number = unscaledWidth;
var h:Number = unscaledHeight;
var cr:Number = getStyle('cornerRadius');
var backgroundFillColors:Array;
var g:Graphics = graphics;
g.clear();
switch( name ){
case "upSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "overSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "downSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
case "disabledSkin":
backgroundFillColors = [0xffffff,0xdddddd];
break;
}
// Draw Background
g.beginGradientFill( GradientType.LINEAR, backgroundFillColors, [1.0,1.0], [0,255], verticalGradientMatrix( 0, 0, w, h ) );
g.drawRoundRectComplex( 0, 0, w, h, cr, cr, cr, cr );
g.endFill();
// Draw other things (borders, icons, etc)
}
}
}
Sometimes I don't use canvas, other times I do. I use things that allow me flexibility inside of the actual asset. However, the idea remains the same, this allows you to set up states for your skin where you can treat things differently depending on the skin.
Personally, I think you should just extend whatever component you use and add states (correctNormal, correctDisabled, wrongNormal, wrongDisable) and just change the state appropriately depending on the properties given to the component. With this, you can specify very easily how everything is suppose to look. Would be even easier if you were using Flex 4.
I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)