Flex Widgets - Inheritance of styles - css

I have a main application (Flex 4.6), in which I intend to use any number of widgets. The widgets are .swf files (s:Module OR s:Application, Flex 4.6).
My problem is that the loaded widget does NOT inherit the styles of the application which is using it.
To put it briefly, I load the widget as an .swf file from the sever (using URLLoader class). After downloading it, I create the instances of the widgets (whereas a single widget can be cointained in the main application on several various places - multiply).
In the main application, the following CSS file is used:
<fx:Style source="css/common.css" />
common.css content is:
s|TextInput {
contentBackgroundColor: #9FD1F2;
focusColor: #8FD7F9;
skinClass: ClassReference("skins.textInputTestSkin");
}
s|Label {
color: #2211FF;
}
And this is how I create and load the widgets:
private var bytesLoader:Loader = null;
public var loadedApp:SystemManager = null;
public var loadedModule:Module = null;
...
bytesLoader.addEventListener(Event.COMPLETE, onBytesLoaderComplete);
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
bytesLoader.loadBytes(urlLoader.data, context);
...
private function onBytesLoaderComplete(e:Event):void {
var dataContent:DisplayObject = bytesLoader.content;
//(Application)
if(dataContent && (dataContent is SystemManager)) {
loadedApp = dataContent as SystemManager;
loadedApp.addEventListener(FlexEvent.APPLICATION_COMPLETE,appWidgetCreationComplete);
appHolder.addChild(dataContent);
} else if(dataContent is IFlexModuleFactory) {
//(Module)
var moduleLoader:LoaderInfo = LoaderInfo(e.target);
moduleLoader.content.addEventListener("ready", moduleWidgetReadyHandler);
}
}
private function moduleWidgetReadyHandler(e:Event):void {
var factory:IFlexModuleFactory = IFlexModuleFactory(e.target);
if(factory) {
loadedModule = factory.create() as Module;
if(loadedModule) {
this.addElement(loadedModule);
}
}
}
My question is first, in what way can I apply the styles of the parents on the widget and secondly(s:Module), in what way is it possible for me to apply the styles of the parents on the widget (s:Application).
UPDATE 1
If I change getter moduleFactory (as seen below) in every single of the widgets, the styles are set just right. Meaning the textInput in the widget (Module and Application) has the same skin as in the main application.
override public function get moduleFactory():IFlexModuleFactory {
return FlexGlobals.topLevelApplication.moduleFactory;
}
It's workaround? It's good solution?

Ok, here is a solution:
Add before bytesLoader.loadBytes(urlLoader.data, context);
//init - moduleFactory
bytesLoader.contentLoaderInfo.addEventListener(Event.INIT, onContentLoaderInfoInit);
private function onContentLoaderInfoInit(e:Event):void {
if(bytesLoader && bytesLoader.contentLoaderInfo) {
bytesLoader.contentLoaderInfo.removeEventListener(Event.INIT, onContentLoaderInfoInit);
}
var loaderInfo:LoaderInfo = LoaderInfo(e.target);
loaderInfo.content.addEventListener(Request.GET_PARENT_FLEX_MODULE_FACTORY_REQUEST, onGetParentModuleFactoryRequest);
}
private function onGetParentModuleFactoryRequest(r:Request):void {
if(isGlobalStyleAllowed) {
if ("value" in r) {
r["value"] = FlexGlobals.topLevelApplication.moduleFactory;
}
}
//remove eventListener
if(bytesLoader && bytesLoader.contentLoaderInfo) {
var loaderInfo:LoaderInfo = LoaderInfo(bytesLoader.contentLoaderInfo);
loaderInfo.content.removeEventListener(Request.GET_PARENT_FLEX_MODULE_FACTORY_REQUEST, onGetParentModuleFactoryRequest);
}
}
It's works.

Related

Xamarin Forms Load Resources on startup

I have an Xamarin Forms app with MvvmCross for Android and IOS and I would like to add a dark theme. My idea was to have to dictionaries with the ressources for either the dark or the light theme and load the one I need on startup.
I added this after I registered the dependencies in my MvxApplication:
if (Mvx.IoCProvider.Resolve<ISettingsManager>().Theme == AppTheme.Dark)
{
Application.Current.Resources.Add(new ColorsDark());
}
else
{
Application.Current.Resources.Add(new ColorsLight());
}
ColorsDark and ColorsLight are my ResourceDictionary. After that i can see the new Dictionary under Application.Current.Resources.MergedDictionaries but the controls can't find the resources as it seems. However it does work when I add it to the App.xaml
<ResourceDictionary Source="Style/ColorsDark.xaml" />
Do I have to put move that another part in the code or is that a wrong approach at all?
Personally don't like this approach at all. What i do: have a static class with all the colors, sizes etc. defined in static fields. At app startup or at app reload after changing skin just call ex: UiSettings.Init() for this ui definitions static class, like follows:
public static class UiSettings
{
public static Init()
{
if (Settings.AppSettings.Skin=="butterfly")
{
ColorButton = Color.Blue;
TitleSize= 12.0;
}
else
if (Settings.AppSettings.Skin=="yammy")
{
ColorButton = Color.Red;
if (Core.IsAndroid)
ButtonsMargin = new Thickness(0.5,0.6,0.7,0.8);
}
// else just use default unmodified field default values
}
public static Color ColorButton = Color.Green;
public static Thickness ButtonsMargin = new Thickness(0.3,0.3,0.2,0.2);
public static double TitleSize= 14.0;
}
in XAML use example:
Color= "{x:Static xam:UiSettings.ColorProgressBack}"
in code use example:
Color = UiSettings.ColorProgressBack;
UPDATE:
Remember that if you access a static class from different assemblies it is possible that you will access a fresh copy of it with default values where Init() didn't happen, if you face such case call Init() from this assembly too.
If you want something to load up when your app loads then you have to code it in App.xaml.cs
protected override void OnStart ()
{
if (Mvx.IoCProvider.Resolve<ISettingsManager>().Theme == AppTheme.Dark)
{
Application.Current.Resources.Add(new Xamarin.Forms.Style(typeof(ContentPage))
{
ApplyToDerivedTypes = true,
Setters = {
new Xamarin.Forms.Setter { Property = ContentPage.BackgroundImageProperty, Value = "bkg7.png"},
}
});
}
else
{
Application.Current.Resources.Add(new Xamarin.Forms.Style(typeof(ContentPage))
{
ApplyToDerivedTypes = true,
Setters = {
new Xamarin.Forms.Setter { Property = ContentPage.BackgroundImageProperty, Value = "bkg7.png"},
}
});
}
}
In this code I'm setting the BackgroungImage of all of my pages. Hope you'll get the idea from this code.

How to load custom font with typesafe css?

I want to load a custom font in a tornadofx-app with typesafe css, is this possible?
Thanks and best regards.
As long as a font is loaded, it can be used in CSS, so we've added a loadFont helper in TornadoFX that can be used like so:
class FontTest : App(Main::class, Styles::class)
class Main : View("Font Test") {
override val root = stackpane {
label("This is my Label") {
addClass(Styles.custom)
}
}
}
class Styles : Stylesheet() {
companion object {
val custom by cssclass()
// Note that loadFont() returns Font?
val riesling = loadFont("/fonts/riesling.ttf", 48.0)
}
init {
custom {
padding = box(25.px)
riesling?.let { font = it }
// or if you just want to set the font family:
// riesling?.let { fontFamily = it.family }
}
}
}
If you know for sure the font exists (e.g. you're including in your build), that can be simplified to:
class Styles : Stylesheet() {
companion object {
val custom by cssclass()
val riesling = loadFont("/fonts/riesling.ttf", 48.0)!!
}
init {
custom {
padding = box(25.px)
font = riesling
// or if you just want to set the font family:
// fontFamily = riesling.family
}
}
}
By the way since 29 days as Ruckus T-Boom answered this question he added the loadFont function :) , with which it's possible to write:
class Styles : Stylesheet() {
private val customFont = loadFont("/fonts/custom-font.ttf", 14)!!
init {
root {
font = customFont
fontSize = 11.px
}
}
}

Flex navigateToURL from iframe POST

Let me explain my current issue right now:
I have a webapp located at domain A. Let's call it A-App. I open an iframe from A-App that points to a Flex app on domain B. We'll call it B-FlexApp. B-FlexApp wants to post some data to another app located on the same domain, we'll call it B-App. The problem is that in IE the communication breaks somewhere between B-FlexApp and B-App while B-FlexApp is opened in the iframe. This only happens in IE.
However when opening B-FlexApp in a new window, posting the data to B-App works just fine. How to overcome this? Dropping the iframe is not possible.
ThereĀ“s a issue with AS3 navigateToURL and IE. You can try calling javascript to navigate: I have a little utility class to handle this:
//class URLUtil
package com
{
import flash.external.*;
import flash.net.*;
public class URLUtil extends Object
{
protected static const WINDOW_OPEN_FUNCTION:String="window.open";
public function URLUtil()
{
super();
return;
}
public static function openWindow(arg1:String = "", arg2:String="_blank", arg3:String=""):void
{
var browserName:String = getBrowserName();
switch (browserName)
{
case "Firefox":
{
flash.external.ExternalInterface.call(WINDOW_OPEN_FUNCTION, arg1, arg2, arg3);
break;
}
case "IE":
{
flash.external.ExternalInterface.call("function setWMWindow() {window.open(\'" + arg1 + "\');}");
break;
}
case "Safari":
case "Opera":
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
default:
{
flash.net.navigateToURL(new URLRequest(arg1), arg2);
break;
}
}
return;
}
private static function getBrowserName():String
{
var str:String="";
var browserName:String = ExternalInterface.call("function getBrowser(){return navigator.userAgent;}");
if (!(browserName == null) && browserName.indexOf("Firefox") >= 0)
{
str = "Firefox";
}
else
{
if (!(browserName == null) && browserName.indexOf("Safari") >= 0)
{
str = "Safari";
}
else
{
if (!(browserName == null) && browserName.indexOf("MSIE") >= 0)
{
str = "IE";
}
else
{
if (!(browserName == null) && browserName.indexOf("Opera") >= 0)
{
str = "Opera";
}
else
{
str = "Undefined";
}
}
}
}
trace("Browser: \t" + str);
return str;
}
}
}
and you call it like:
btn.addEventListener(MouseEvent.CLICK, onBTNClick);
function onBTNClick(evt:MouseEvent):void
{
URLUtil.openWindow(YOUR_URL_STRING);
}
Hope it helps!
It is better to let the browser actually does the "navigate to URL" function instead of Flex.
For example, in the page that contains the Flex app, the page would contain a Javascript function call handleNavigationRequest(pageName, target). In the Flex application, you may utilize ExternalInterface, and call the handleNavigationRequest.
By using this paradigm, the Flex application would not have to figure the details as to how the external implementations such as frame setup, etc, and you end up having a cleaner and less-coupled design.
I've found out that i can use swfObject to embed the flash object thus the iframe implementation is completely useless. Embedding the flash component in the overlay, instead of opening it in an iframe, makes IE behave properly.
I had the same problem and I solved it simply passing the second argument (browser window) to the function:
navigateToUrl(url,"_blank"); , in my case I use "_blank".
It works with IE8 and IE9.
Davide

embedding a Movieclip with flashDevelop

I am trying to embed a movieClip with flashDevelop but apparently its not working because its not recognizing the movieclips that are within it. here is my code
package com.objects{
import flash.display.MovieClip
import flash.text.TextField;
import flash.events.*;
import flash.text.TextFormat;
[Embed(source='../../../lib/fighter.swf', symbol='InfoBox')]
public class InfoBox extends gameObject {
protected var textf:TextField;
protected var s:String;
public var type:Boolean = false;
private var letter:Number = 0;
private var string:Array;
private var line:Number = 0;
private var portNum:Array;
public function InfoBox():void
{
portNum = new Array();
portrait.stop();
x = 400;
y = 550;
string = new Array();
var format = new TextFormat();
format.size = 18;
textf = new TextField();
textf.width = 550;
textf.height = 85;
textf.x = -220;
textf.y = -60;
textf.wordWrap = true;
textf.defaultTextFormat = format;
addChild(textf);
contButton.addEventListener(MouseEvent.MOUSE_DOWN, StoryNext);
}
private function StoryNext(e:MouseEvent):void
{
if(string.length > 1)
{
portNum.splice(0,1);
string.splice(0,1);
trace(string.length);
letter = 0;
}
else
{
contButton.removeEventListener(MouseEvent.MOUSE_DOWN, StoryNext);
dispatchEvent(new Event("StoryContinue"));
}
}
public function textInfo(msg:String,num:Number = 1):void
{
string.push(msg);
portNum.push(num);
}
override public function updateObject():void
{
TypeWords();
}// End UpdateObject
public function TypeWords():void
{
if(type)
{
portrait.gotoAndStop(portNum[0]);
var s:String = string[0];
if(letter <= s.length)
{
textf.text = s.substring(0,letter);
}
letter++
}
}
}
}
and I am getting this error
C:\Users\numerical25\Documents\RealGames\FighterPilot\beta1FlashDev\src\com\objects\InfoBox.as(23): col: 4 Error: Access of undefined property portrait.
portrait is a movie clip that I have inside of the info box movie clip. it is already on the stage and i gave it a instance name of portrait. It worked in flash, but now its not in flashDevelop
Try accessing the childs by name. It should work.
(getChildByName("portrait") as MovieClip).gotoAndStop(portNum[0]);
You need to define the corresponding properties on your class. In this case you can add:
public var portrait:MovieClip;
If it's some other type, for instance a Sprite you can change the definition to that.
EDIT: If you're having trouble setting up Flash Develop, I wrote some tutorials on that
[Embed(source='../../../lib/fighter.swf',
symbol='InfoBox')]
I'd say use SWCs not SWFs, it will make it a ****load easier.
SWCs keep all the stuff in place, whereas the swf can remove assets that are not used to save room.
Also in your flash develop panel you will see the swc (like the swf) and see all the classes that are included. Just right-click on it and add it to library. You won't have to use the [embed] code.
give that a try first, unless you absolutely need an swf.
To create an swc, use the flash tab under publish settings.
you can add linkage name for your MovieClip and export it as SWC, suppose named "mymc"
then copy SWC file to you FD project.
in your code, just addChild(new mymc());

How does one define a default style for a custom Flex component?

I'm creating a new Flex component (Flex 3). I'd like it to have a default style. Is there a naming convention or something for my .cs file to make it the default style? Am I missing something?
Christian's right about applying the CSS, but if you're planning on using the component in a library across projects, you're gonna want to write a default css file for that library. Here's how you do it:
Create a css file called "defaults.css" (Only this file name will work!) and put it at the top level under the "src" folder of your library. If the css file references any assets, they have to be under "src" as well.
(IMPORTANT!) Go to library project's Properties > Flex Library Build Path > Assets and include the css file and all assets.
That's how the Adobe team sets up all their default styles, now you can do it too. Just figured this out- huge
Two ways, generally. One's just by referencing the class name directly -- so for example, if you'd created a new component class MyComponent in ActionScript, or indirectly by making an MXML component extending another UIComponent called MyComponent, in both cases, the component would pick up the styles declared in your external stylesheet, provided that stylesheet's been imported into your application (e.g., via Style source):
MyComponent
{
backgroundColor: #FFFFFF;
}
Another way is by setting the UIComponent's styleName property (as a string):
public class MyComponent
{
// ...
this.styleName = "myStyle";
// ...
}
... and defining the style in the CSS file like so (note the dot notation):
.myStyle
{
backgroundColor: #FFFFFF;
}
Make sense?
In addition to what Christian Nunciato suggested, another option is to define a static initializer for your Flex component's styles. This allows you to set the default styles without requiring the developer to include a CSS file.
private static function initializeStyles():void
{
var styles:CSSStyleDeclaration = StyleManager.getStyleDeclaration("ExampleComponent");
if(!styles)
{
styles = new CSSStyleDeclaration();
}
styles.defaultFactory = function():void
{
this.exampleNumericStyle = 4;
this.exampleStringStyle = "word to your mother";
this.exampleClassStyle = DefaultItemRenderer //make sure to import it!
}
StyleManager.setStyleDeclaration("ExampleComponent", styles, false);
}
//call the static function immediately after the declaration
initializeStyles();
A refinement of what joshtynjala suggested:
public class CustomComponent extends UIComponent {
private static var classConstructed:Boolean = classConstruct();
private static function classConstruct():Boolean {
if (!StyleManager.getStyleDeclaration("CustomComponent")) {
var cssStyle:CSSStyleDeclaration = new CSSStyleDeclaration();
cssStyle.defaultFactory = function():void {
this.fontFamily = "Tahoma";
this.backgroundColor = 0xFF0000;
this.backgroundAlpha = 0.2;
}
StyleManager.setStyleDeclaration("CustomComponent", cssStyle, true);
}
return true;
}
}
I've read this in the docs somewhere; the classContruct method gets called automatically.
You may want to override default styles using the <fx:Style> tag or similar. If that's the case, a CSSStyleDeclaration may already exist by the time classConstructed is checked. Here's a solution:
private static var classConstructed:Boolean = getClassConstructed ();
private static function getClassConstructed ():Boolean {
var defaultCSSStyles:Object = {
backgroundColorGood: 0x87E224,
backgroundColorBad: 0xFF4B4B,
backgroundColorInactive: 0xCCCCCC,
borderColorGood: 0x333333,
borderColorBad: 0x333333,
borderColorInactive: 0x666666,
borderWeightGood: 2,
borderWeightBad: 2,
borderWeightInactive: 2
};
var cssStyleDeclaration:CSSStyleDeclaration = FlexGlobals.topLevelApplication.styleManager.getStyleDeclaration ("StatusIndicator");
if (!cssStyleDeclaration) {
cssStyleDeclaration = new CSSStyleDeclaration ("StatusIndicator", FlexGlobals.topLevelApplication.styleManager, true);
}
for (var i:String in defaultCSSStyles) {
if (cssStyleDeclaration.getStyle (i) == undefined) {
cssStyleDeclaration.setStyle (i, defaultCSSStyles [i]);
}
}
return (true);
}
To create a default style you can also have a property in your class and override the styleChanged() function in UIComponent, eg to only draw a background color across half the width of the component:
// this metadata helps flex builder to give you auto complete when writing
// css for your CustomComponent
[Style(name="customBackgroundColor", type="uint", format="color", inherit="no")]
public class CustomComponent extends UIComponent {
private static const DEFAULT_CUSTOM_COLOR:uint = 0x00FF00;
private var customBackgroundColor:uint = DEFAULT_CUSTOM_COLOR;
override public function styleChanged(styleProp:String):void
{
super.styleChanged(styleProp);
var allStyles:Boolean = (!styleProp || styleProp == "styleName");
if(allStyles || styleProp == "customBackgroundColor")
{
if(getStyle("customBackgroundColor") is uint);
{
customBackgroundColor = getStyle("customBackgroundColor");
}
else
{
customBackgroundColor = DEFAULT_CUSTOM_COLOR;
}
invalidateDisplayList();
}
// carry on setting any other properties you might like
// check out UIComponent.styleChanged() for more examples
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
graphics.clear();
graphics.beginFill(customBackgroundColor);
graphics.drawRect(0,0,unscaledWidth/2,unscaledHeight);
}
}
You could also create a setter for the customBackgroundColor that called invalidateDisplayList(), so you could also set the customBackgroundColor property programatically as well as through css.

Resources