Again, a theming question. Because the project I'm working on requires older libraries that extend mx components (such as TitleWindow and TabNavigator), I can't use what I know about Spark skinning directly. However, since the project is being programmed using the default Spark theme (with my modifications on top) rather than the Halo theme, I apparently don't have access to the styles I need (namely backgroundImage and contentBackgroundImage which apparently require Halo to be active). Simply setting Halo to be the theme will break other things, not the least of which my own theme. Plans are in the works to replace the older libraries or at least patch them better to Flex 4, but as of right now, I need a way to style/skin these components without modifying them directly.
It would be ridiculous to be unable to add a background image to a TitleWindow's content area! I've searched the internet high and low all day and tried countless variations of styles, skins, selectors, and combinations thereof with no luck. Doesn't anyone know how to add a background image to the content of a mx TitleWindow while using the Flex 4.1 sdk?!
Actually, it's not the only way, it's -as you've mentioned- the hardcoded way: sorry about that.
You can also skin your TitleWindow component to accept background images.
To create the appropriate skin with all the necessary states, you can copy the base skin: spark.skins.spark.TitleWindowSkin as MyTitleWindowSkin, and add some customization to it:
In the MetaData tag you should enter the name of your custom TitleWindow class:
<fx:Metadata>
<![CDATA[
[HostComponent("my.package.CustomTitleWindow")]
]]>
</fx:Metadata>
To accept backgroundImage,
you should declare a variable:
[Bindable] private var
backgroundImage:*;
override the
updateDisplayList(unscaledWidth,
unscaledHeight) method, and inside
of it initialize this member:
backgroundImage =
getStyle("backgroundImage");
in the <!-- layer 2: background fill
--> section, after the solid-color-fill (<s:Rect
id="background"...), you should put
the following snippet:
<s:Rect id="backgroundImg"
left="1" right="1"
top="{topGroup ? topGroup.height : 0}"
bottom="{bottomGroup ? bottomGroup.height : 0}">
<s:fill>
<!-- BackgroundImage -->
<s:BitmapFill id="img" source="{backgroundImage}"
smooth="true" fillMode="scale" />
</s:fill>
</s:Rect>
Next you need to create a new class (my.package.CustomTitleWindow), that extends TitleWindow, set its skin, and bind the backgroundImage style:
<?xml version="1.0" encoding="utf-8"?>
<s:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
skinClass="my.package.MyTitleWindowSkin">
<fx:Metadata>
[Style(name="backgroundImage", type="*")]
</fx:Metadata>
<mx:VBox width="100%" height="100%">
<mx:Text text="{IMyConstants.LOREMIPSUM}" width="100%" height="100%" />
<s:Button label="Do something" />
</mx:VBox>
</s:TitleWindow>
at the end a small test (which worked fine at my side, and i hope it's closer to what you're looking for):
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10">
<my:CustomTitleWindow title="Window without background image"
width="100%" height="50%" />
<my:CustomTitleWindow title="Window with background image"
width="100%" height="50%" backgroundImage="{IMyConstants.MYLOGO}" />
</s:VGroup>
Update
For setting the skin and the background image from a css file, you only need some minor modifications:
Create a CSS file with content:
/* CSS file */
#namespace s "library://ns.adobe.com/flex/spark";
#namespace mx "library://ns.adobe.com/flex/mx";
#namespace my "your.package.*";
my|CustomTitleWindow {
skin-class: ClassReference("your.package.MyTitleWindowSkin");
}
.twWithBgImage {
background-image: Embed("icons/logo.png");
}
The test would look like:
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10">
<my:CustomTitleWindow title="Window without background image"
width="100%" height="50%" />
<my:CustomTitleWindow title="Window with background image"
width="100%" height="50%" styleName="twWithBgImage" />
</s:VGroup>
and you need to remove the skin declaration from the CustomTitleWindow class: skinClass="your.package.MyTitleWindowSkin".
Of course you don't need to apply the skin to the my|CustomTitleWindow class, you could use it just for a css class, this way you surely don't need to modify your existing component.
Update -- without custom component
Forget the CustomTitleWindow class.
skinnedtw.css
/* CSS file */
#namespace s "library://ns.adobe.com/flex/spark";
#namespace mx "library://ns.adobe.com/flex/mx";
.twWithBgImage {
skin-class: ClassReference("your.package.MyTitleWindowSkin");
background-image: Embed("icons/logo.png");
}
TestApp.mxml
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Style source="assets/skinnedtw.css" />
<s:VGroup width="100%" height="100%" paddingLeft="10" paddingTop="10" paddingRight="10">
<s:TitleWindow title="Window without background image"
width="100%" height="50%">
<mx:VBox width="100%" height="100%">
<mx:Text text="{IMyConstants.LOREMIPSUM}" width="100%" height="100%" />
<s:Button label="Do something" />
</mx:VBox>
</s:TitleWindow>
<s:TitleWindow title="Window with background image"
width="100%" height="50%" styleName="twWithBgImage">
<mx:VBox width="100%" height="100%">
<mx:Text text="{IMyConstants.LOREMIPSUM}" width="100%" height="100%" />
<s:Button label="Do something" />
</mx:VBox>
</s:TitleWindow>
</s:VGroup>
</s:WindowedApplication>
My output still looks like this:
if you don't have explicit members in your mx:TitleWindow, than you should consider using
a spark BorderContainer as its first child, since you can specify a background image to that.
i'm thinking of something like this:
<?xml version="1.0" encoding="utf-8"?>
<mx:TitleWindow xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" layout="absolute"
width="400" height="300" backgroundAttachment="">
<s:BorderContainer backgroundImage="{IMyConstants.MYLOGO}"
width="100%" height="100%" backgroundAlpha=".5" />
<mx:VBox width="100%" height="100%">
<mx:Text text="{IMyConstants.LOREMIPSUM}" width="100%" height="100%" />
<mx:Button label="Do something" />
</mx:VBox>
</mx:TitleWindow>
i hope i understood your problem, and this helps.
Related
What To Achieve
I'm trying to apply a rounded corner to an Image retrieved from a URL by using a BorderContainer (SPARK).
The Problem
The Radius attribute of the BorderContainer component is only being applied when running the code from my debugger, running it elsewhere does simply not apply the radius to the bitmap image.
Code Example
<s:BitmapImage id="img" width="60" height="60" source="{data.IconURL}" smooth="true" smoothingQuality="high">
<s:mask>
<s:BorderContainer id="bmpMask" cornerRadius="15" width="60" height="60" borderVisible="true" />
</s:mask>
</s:BitmapImage>
Image Examples
1. As it should be (And is in the debugger only):
As it is:
Full working example:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
<s:BitmapImage source="https://www.dropbox.com/s/j5wu759l1tkiw2b/archer.jpg?dl=1"
height="100" width="100" maskType="alpha">
<s:mask>
<s:BorderContainer id="bmpMask" cornerRadius="15" width="60" height="60"
borderVisible="true"/>
</s:mask>
</s:BitmapImage>
</s:Application>
I made a simple test Air application to try different approaches to masking or using hitArea to ignore mouse events over transparent areas of a PNG. Can't seem to find the right mix of things to make it work, nor could I find a succinct example on the web.
Clicking on the transparent areas of any of these methods don't result in the click getting handled by the background.
Here's the code I have:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
status="{clicked}">
<fx:Declarations>
<s:Image id="maskX" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</fx:Declarations>
<fx:Script>
<![CDATA[
[Bindable] public var clicked:String = "none";
protected function onClick(event:MouseEvent):void
{
clicked = event.currentTarget["id"] + "\t" + (new Date()).time;
}
]]>
</fx:Script>
<!-- Some sort of background so I can see transparency working. -->
<s:Group id="background" width="100%" height="100%" click="onClick(event)">
<s:Rect width="100%" height="100%">
<s:fill>
<s:LinearGradient rotation="90">
<s:GradientEntry color="blue"/>
<s:GradientEntry color="white"/>
</s:LinearGradient>
</s:fill>
</s:Rect>
</s:Group>
<s:Group width="100%" height="100%">
<s:layout>
<s:TileLayout/>
</s:layout>
<!-- Simple attempt at having Flex respect the alpha in the PNG itself as transparent
with regard to clicks -->
<s:Group id="image1" click="onClick(event)" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Use an explicit bitmap mask for the hitArea -->
<s:Group id="image2" click="onClick(event)" hitArea="{mask2}" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')"/>
<s:Image id="mask2" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Try using just the mask bitmap -->
<s:Group id="image3" click="onClick(event)" hitArea="{mask3}" mouseEnabledWhereTransparent="false">
<s:Image id="mask3" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:Group>
<!-- Specify the hitArea with alternate syntax -->
<s:Group id="image4" click="onClick(event)" mouseEnabledWhereTransparent="false">
<s:Image source="#Embed('image1.png')"/>
<s:hitArea>
<s:Image id="mask4" source="#Embed('mask1.png')" cacheAsBitmap="true"/>
</s:hitArea>
</s:Group>
</s:Group>
The image1 and mask1 files I've uploaded here:
image1 - http://img853.imageshack.us/img853/923/image1yj.png
mask1 - http://img715.imageshack.us/img715/3755/mask1.png
Actually, it is possible. Here is a sample:
http://www.webverwirklichung.com/en/blog/programming/flex/creating-hitarea-png-image-transparent-alpha-regions-flex
Flex doesn't respect the alpha channel of a PNG, but you can render out the visible content into a sprite, and use that as a mask on any DisplayObject. Using this approach, only the visible area of the ping will cause mouse events, and it should respect all opacity. If you are layering things, you might hit a few issues.
Just make sure you use the alpha channel for masking content, not a specific color channel.
I am trying to implement some MVC-style UI components in Flex 4. I want to further separate the visual setup (and later runtime modification) of each element. Primarily, I want to keep the overall layout of the data like such in the MXML file that will USE the skin:
<s:Group id="container">
<s:VGroup>
<s:HGroup id="titleGroup">
<s:Label id="titleText" />
<s:Button id="closeButton" />
</s:HGroup>
<s:HGroup id="contentGroup">
<s:VGroup id="interactionGroup">
<s:VGroup id="messageGroup" />
<s:HGroup id="actionGroup" />
</s:VGroup>
</s:HGroup>
</s:VGroup>
</s:Group>
And in the Skin file, I want to be able to refer to the ids and set properties on them (such as width and height on the "container" group) AND be able to include graphical elements within them (such as a filled rect inside the "container" group). I want to do this declaratively, NOT programmatically, else why use MXML in the first place? This is the way that HTML/CSS is divided up: HTML contains what the data IS relative to other data, and the CSS contains all the properties and visual styles.
As it is now, my skin looks like this:
<s:Group id="container" verticalCenter="0" horizontalCenter="0">
<s:Rect id="background" width="100%" height="100%" radiusX="10" radiusY="10">
<s:filters>
<s:DropShadowFilter blurX="20" blurY="20" alpha="0.32" distance="5" angle="90" />
</s:filters>
<s:fill>
<s:SolidColor color="#ffffff" />
</s:fill>
</s:Rect>
<s:VGroup width="100%" height="100%" gap="3" paddingTop="10" paddingBottom="10" paddingLeft="10" paddingRight="10">
<s:HGroup id="titleGroup" width="100%" verticalAlign="middle">
<s:Label id="titleText" width="100%" />
<s:Button id="closeButton" />
</s:HGroup>
<s:HGroup id="contentGroup" width="100%">
<s:VGroup id="interactionGroup" width="100%">
<s:VGroup id="messageGroup" width="100%" />
<s:HGroup id="actionGroup" width="100%" />
</s:VGroup>
</s:HGroup>
</s:VGroup>
</s:Group>
And the actual component that uses this skin is nothing more than SkinPart declarations:
<fx:Script>
<![CDATA[
import spark.components.Label;
import spark.components.Button;
import spark.components.Group;
[SkinPart(required="true")]
public var titleText:Label;
[SkinPart(required="true")]
public var closeButton:Button
[SkinPart(required="true")]
public var messageGroup:Group;
[SkinPart(required="true")]
public var actionGroup:Group;
]]>
</fx:Script>
As you can see, the Skin contains everything about the component, violating my sense of separating style and content. The using element has nothing in it except for interfaces to the Skin for programmatic changes. My current solution has no advantage over not using skins at all, except for the slightly easier process of switching out skins.
Is there a way to do what I have proposed in Flex 4?
The answer is that Flex does not work this way. Layout and visual style appear to be inseparably linked. Closing out this question.
I discovered this while doing some programmatic panel resizing:
Components in a spark Panel will still be visible when their location is outside the physical Panel boundaries. This does not happen with the mx Panel.
Running Flex 4.1 on Windows 7
I tried putting mx and spark controls in the spark Panel, and they both appear outside of the boundaries. Note this doesn't happen with the mx Panel.
What am I missing to make the spark behave like the mx?
Thanks !
Sample Code:
<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Panel x="6" y="8" width="157" height="200">
<s:Label x="2" y="10" text="ABCDEFGHIJKL" width="258" height="35" textAlign="right"/>
<mx:Label text="Label" x="232" y="55"/>
<mx:Button x="125" y="96" label="Button"/>
</s:Panel>
<mx:Panel x="10" y="216" width="200" height="200" layout="absolute">
<mx:Label x="0" y="46" text="Label" width="217" textAlign="right"/>
<mx:Button x="163" y="88" label="Button"/>
</mx:Panel>
</s:WindowedApplication>
You may put a s:Group in them and set clipAndEnableScrolling="true". It's quite similar to CSS's overflow:hidden.
I have the following application and I am trying to put a scrollbar somewhere inside the TabNavigator, preferably on the innermost Vbox, but the scrollbar always ends up on the Panel. Is there some kind of property which controls this? I thought height=100% would have controlled it but it doesn't seem to work.
Sample is here. You can view source on it:
VBox Sample
Here is the source anyway:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" viewSourceURL="srcview/index.html">
<mx:Panel width="400" height="400">
<mx:TabNavigator width="100%" height="100%" creationPolicy="all">
<mx:VBox label="Tab 1" width="100%" height="100%">
<mx:ViewStack width="100%" height="100%">
<mx:VBox width="100%" height="100%">
<mx:Text width="100%" height="500" text="This box is taller than the Panel, but the scrollbar is on the window." />
</mx:VBox>
</mx:ViewStack>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
I found the proper solution is to set the minheight on the box to 0. When the minheight is not defined, the box will try to remain at it's measured height. By setting the minheight you are telling the box it is ok to shrink.
I've had this issue in the past, and the solution is a bit wonky, but hopefully this helps:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical">
<mx:Panel width="400" height="400" verticalScrollPolicy="off">
<mx:TabNavigator width="100%" height="100%">
<mx:VBox id="container" label="Tab 1" width="100%" height="100%">
<mx:ViewStack width="100%" height="{container.height - 20}">
<mx:VBox width="100%" height="100%">
<mx:Text width="100%" height="500" text="This box is taller than the Panel, but the scrollbar is on the window." />
</mx:VBox>
</mx:ViewStack>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
Flex seems to calculate dynamic component dimensions properly about 98% of the time, but sometimes we need to "massage" the logic.
When you look at your layout hierarchy (as I've shown above), you just need to name the parent container (in this case Tab1) for the purposes of data binding, then use that container's height property for your sizing. (you also don't need to explicitly state verticalScrollPolicy="off" on the panel, but I use if for good measure :P)
Note that you may need to subtract a bit from the given height or your scrollbar will be below the component bounds ;)