Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > e4xu

Всякие разные штуки сомнительной полезности сделанные в свободное от работы время.
Рейтинг: 4.67. Голосов: 3.

Пользовательские мета-теги.

Запись от wvxvw размещена 02.05.2009 в 22:30
Обновил(-а) wvxvw 03.05.2009 в 17:21

Во-первых, спасибо Philippe с http://www.flashdevelop.org/ за наводку.
Во-вторых, конечно, если вы и раньше об этом знали, вам будет не интересно.
Оказывается, используя Flex SDK можно определить пользовательские мета-теги. Вам наверняка знакомы [Embed], [Bindable], [Event] и еще мног разных документированых и не очень тегов. А сейчас мы попробуем разобраться, как создать свой тег и как его потом использовать.

Цель проекта: мне очень нравится возможность использования стилей во Флексовых приложениях, но необходимость ради этого импортировать менеджер стилей и все сопутствующие классы удручает Да и вообще, зачем пользоваться чем-то готовым, если можно написать по-своему, изобрести велосипед в который раз и получить удовольствие от процесса?
В аттачменте готовый FD проект *.

Для начала скомпилируем SWC, принципиальным является указать -keep-as3-metadata=<имя метатега> в дополнительных аргументах компилятора.
Вот мои настройки, вам нужно будет изменить пути так, чтобы они соответствовали вашему проекту:
Билд-файл:
Код:
<?xml version="1.0" encoding="utf-8"?>
<flex-config>
  <compiler>
    <source-path append="true">
      <path-element>C:\www\projects\csstest\SWC-sources</path-element>
      <path-element>C:\Program Files\FlashDevelop\Library\AS3\classes</path-element>
    </source-path>
  </compiler>
  <include-classes>
	<class>ICSSClient</class>
  </include-classes>
</flex-config>
BAT-файл, с помощью которого и компилируем.
Код:
set sdk=C:\Flex_sdk_3_3\bin\compc.exe
set buildfile=C:\www\projects\csstest\run\buildSWC.xml
set outputfile=C:\www\projects\csstest\lib\lib.swc
set exec=%sdk% -load-config+=%buildfile% -debug=true -incremental=true -benchmark=false -output=%outputfile% -strict=true -keep-as3-metadata=CSS
call %exec%
pause
Абсолютно не обязательно чтобы ICSSClient содержал тег [CSS]*.
Подключаем скомпилированый SWC к проекту - все, можно использовать наш тег

Следующим шагом создадим класс, который будет этот тег использовать:
Код AS3:
package  
{
	//{imports
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.utils.getQualifiedClassName;
	//}
 
	// See the rectangular shape drawn in different coordinates
	// with different dimensions.
	[CSS(bounds="0,0,100,100")] 
	// Violet, see it is set to yellow once we apply styling.
	[CSS(color="#FF00FF")] 
 
	public class CSSSprite extends Sprite implements ICSSClient
	{
		private var _bounds:String;
 
		[Bindable("boundsChange")]
 
		/**
		* ...
		* This property can be used as the source for data binding.
		* When this property is modified, it dispatches the <code>boundsChange</code> event.
		*/
		public function get bounds():String { return _bounds; }
 
		public function set bounds(value:String):void 
		{
		   if (_bounds == value) return;
		   _bounds = value;
		   var temp:Array = value.split(/\s*,\s*/);
		   boundsRect = new Rectangle(Number(temp[0]), Number(temp[1]), 
							Number(temp[2]), Number(temp[3]));
		   dispatchEvent(new Event("boundsChange"));
		   // You usually would like to deffer draw() until
		   // the next screen update, but we don't want to 
		   // overcomplicate this example.
		   draw();
		}
 
		private var _color:String;
 
		[Bindable("colorChange")]
 
		/**
		* ...
		* This property can be used as the source for data binding.
		* When this property is modified, it dispatches the <code>colorChange</code> event.
		*/
		public function get color():String { return _color; }
 
		public function set color(value:String):void 
		{
		   if (_color == value) return;
		   _color = value;
		   colorStyle = parseInt(value.match(/[\dABCDEF]+/ig)[0], 16);
		   dispatchEvent(new Event("colorChange"));
		   draw();
		}
 
		protected var boundsRect:Rectangle;
		protected var colorStyle:uint;
 
		public function CSSSprite() 
		{
			super();
			CSSManager.styleObject(this);
		}
 
		/* INTERFACE ICSSClient */
 
		public function get className():String
		{
			var temp:String = getQualifiedClassName(this);
			if (temp.indexOf(":") < 0) return temp;
			return temp.replace(/.*([^:]+$)/, "$1");
		}
 
		public function draw():void
		{
			// we don't set initializes, though, usually you'd like to have those
			// so you could draw as soon as the object is created.
			if (!boundsRect) return; 
			graphics.clear();
			graphics.beginFill(colorStyle);
			graphics.drawRect(boundsRect.x, boundsRect.y, 
						boundsRect.width, boundsRect.height);
			graphics.endFill();
		}
	}
}
Затем, класс, который осуществляет парсинг наших тегов.
Код AS3:
package  
{
 
	//{imports
	import flash.utils.describeType;
	//}
 
	public class CSSManager 
	{
		// Just some test CSS declaration. You will probably want to expand that 
		// to be embedded or loaded at runtime.
		private static var _styles:XML =
		<![CDATA[
		AnotherStyle
		{
			foo: bar;
		}
		CSSSprite 
		{
			bounds: 50, 100, 150, 150;
			color: #FFFF00;
			some-unrelated-property: whatever
		}
		YetAnotherStyle {}
		]]>;
 
		public function CSSManager() { super(); }
 
		public static function styleObject(object:ICSSClient):void
		{
			var className:String = object.className;
			// We simplify CSS parsing as much as possible.
			// You will probably want to implement
			// your own parsing routine.
			var declarations:String = _styles.toString().replace(/[\r\n\t]*/g, "");
			var declarationRE:RegExp = new RegExp(className + "\\s*\\\{[^\\\}]*\\\}", "g");
			var declaration:String = declarations.match(declarationRE)[0];
			declaration = declaration.match(/([^\}\{]*)\}/)[1];
			var props:Object = { };
			var propsDeclarations:Array = declaration.split(";");
			var prop:String;
			var val:String;
			var pair:Array;
			// Here we look for the metadata declarations stored with our class.
			var applicableStyles:XMLList = describeType(object).metadata.(@name == "CSS").arg;
			var keys:Array = [];
			applicableStyles.@key.(keys.push(toXMLString()));
			var p:String
			for each(p in propsDeclarations)
			{
				pair = p.split(":");
				prop = pair[0];
				val = pair[1];
				// Filter properties that aren't related to our style client.
				// We could also check here for default values, 
				// e.g. if the value in <code>@value</code> attribute is the same as the one 
				// we want to assign, we may also skip that style property.
				if (keys.indexOf(prop) < 0) continue;
				props[prop] = val;
			}
			for (p in props)
			{
				// Here you want to parse values from string and convert them
				// To the appropriate types.
				// But for the sake of simplicity we will assigns
				// strings and handle the parsing on the ICSSClient's side.
				object[p] = props[p];
			}
		}
	}
}
Дополнение

Попробую более подробно объяснить. На самом деле это не сложно, я, наверное просто очень запутано изложил.

Для начала, исходная информация:
http://www.adobe.com/cfusion/communi...2&postId=11907
То, что я сделал, это пример практического применения.

Задача:
Создать свой мета-тег [CSS], который мы впоследстии сможем использовать в нашем AS3 или MXML проекте для того, чтобы задавать стили нашим визуальным компонентам. Что-то похожее на уже существующий тег [Style] только очень упрощенный.
Использовать мы его будем так:
- [CSS(propertyName="defaultValue")]
- для этого нам обязательно в классе применяющем этот тег нужно будет объявить свойство с именем propertyName типа String (это в нашем примере, но вы наверняка захотите это переделать по-своему, чтобы парсить стили в менеджере, а в классе использующем стиль задавать свойства не в виде строки, а уже в виде правильного типа).
Т.е. нам нужно будет такое свойство:
Код AS3:
protected var _propertyName:Type;
public function set propertyName(value:String):void
{
    _propertyName = valueFromString(value, Type) as Type;
}
protected function valueFromString(value:String, type:Class):Object
{
    switch (type)
    {
        case Number:
            return parseFloat(value);
        case Type:
            return new Type(value); // Предположим, что объекты типа Type создаются именно таким образом.
        .... // наверняка разных типов будет больше.
    }
    return null;
}
- И последнее, что нам для этого будет нужно - класс, который распарсит стили и метаданные и вызовет этот сеттер. Например так:
Код AS3:
CSSManager.setStyleToClient(theClient:ICSSClient);
в этом методе CSSManager прочитает метаданные доступные для этого объекта, потом прочитает CSS стили, найдет стиль соотвествующий theClient и задаст propertyName свойству значение из стиля.

Решение:
- Чтобы наши мета-теги вообще записались в декларацию класса нам нужно будет скомпилировать SWC с параметром -keep-as3-metadata=CSS. Потом подключить этот CSS к проекту и использовать какой-нибудь класс или интерфейс из этого SWC. (Не объязательно использовать мета-тег в классе, который компилируется в SWC, даже, скорее, не нужно этого делать т.как тестировать будет неудобно).
Для этого мы компилируем интерфейс ICSSClient в SWC, подключаем SWC к проекту и имплементим этот интерфейс в CSSSprite. (Не оставляйте ICSSClient.as в зоне видимости компилятора т.как AS файлы имеют приоритет при компиляции, т.е. если вы оставите этот интерфейс на classpath, то скомпилируется именно он, а не тот, что в SWC).
- Создаем класс CSSSprite который использует тег [CSS].
- Создаем класс CSSManager который парсит CSS и мета данные, а так же инициализирует свойства связаные со стилями.
Мета-данные мы можем получить распарсив XML возвращаемый describeType(). Например, данные по классу будут храниться в описании класса:
Код:
<factory type="CSSSprite">
    <metadata name="CSS">
      <arg key="propertyName" value="defaultValue"/>
    </metadata>
....
</factory>
Мета-данные так же можно дописать и методу и свойству. *
Дальше применив RegExp и E4X мы найдем какие же свойства нам нужно инициализировать, и какие значения им нужно присвоить.
Инициализируем свойства CSSSprite'а значениями получеными из CSS.
Вложения
Тип файла: zip csstest.zip (15.8 Кб, 196 просмотров)
Размещено в Frameworkless MXML
Комментарии 10 Отправить другу ссылку на эту запись
Всего комментариев 10

Комментарии

Старый 03.05.2009 03:08 iNils вне форума
iNils
 
Аватар для iNils
Цитата:
Для начала скомпилируем SWC, принципиальным является указать -keep-as3-metadata=<имя метатега> в дополнительных аргументах компилятора.
swc чего? Как-то не ясно.
Старый 03.05.2009 05:19 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Вот, это то, что компилируем в SWC, но, вообще это может быть любой класс / интерфейс при условии, что мы его потом из этого SWC будем использовать
Код AS3:
package  
{
	public interface ICSSClient 
	{
		function get className():String;
	}
}
Поудалял технические комментарии.
Старый 03.05.2009 15:10 Wolsh вне форума
Wolsh
 
Аватар для Wolsh
wvxvw, перечитал три раза и понял, что тема интересная))) Не мог бы ты написать об этом же, но понятно? Я конечно понимаю, что "понятно" - понятие субъективное, но это похоже на конспект гения, найденный наследником-балбесом... Я чувствую, что тут пахнет золотом, но разобраться одновременно в двух лабиринтах - твоей мысли и твоего кода - для меня мучительно трудно. Нельзя ли слегка расширить этот текст по высоте, объяснив - задачу, которую ты решаешь, метод ее решения по шагам(чтобы решить нашу задачу, нам надо сначала... и т.д.), и результат (это можно вкратце и с восторгом)). Был бы очень признателен, обожаю ФД и мне это все очень интересно))) Спасибо.
(ээ.. понимаешь, когда например смотришь чужой класс - скачанный как ресурс к статье, выложенный автором с превьюшкой - по крайней мере, имеешь представление о том, что он делает. а здесь надо разобрать твой код и понять, что же ты хотел сделать. Учитывая, что при этом ты рассказываешь о НОВОМ НЕИЗВЕДАННОМ способе это делать - задача становится нерешаемой. Может не только для меня.)
Обновил(-а) Wolsh 03.05.2009 в 15:17
Старый 03.05.2009 17:15 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Написал дополнение. Возможно просто мой пример не самый удачный... надо было подумать о чем-то более наглядном...
Старый 03.05.2009 21:00 Wolsh вне форума
Wolsh
 
Аватар для Wolsh
О, теперь многое стало понятней! Спасибо, удачи!
Старый 05.05.2009 07:54 etc вне форума
etc
 
Аватар для etc
Wolsh, способ ещё в 2006 (или 2007-ом) году был описан

Используем на всю катушку уже давно.
Старый 05.05.2009 14:36 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
@ __etc:
Если чесно, то я не нашел ни одного описания на русском языке. Может плохо искал. А так вообще, я думаю, что способ уже был во втором Флексе. И вообще по метадате очень мало информации... Т.е. даже "документированые" теги документированы не полностью, так что, я думаю, не удивительно, что кто-то этого не знает

Да, и еще, ты говорил, что используете внешний CSS. А как вы его загружаете: в рантайме, или эмбедите? Я тут думал для самообразования попробовать написать на Яве транскодер для стилей. Просто интересно, может кто-то уже это сделал
Обновил(-а) wvxvw 05.05.2009 в 18:01
Старый 02.08.2009 13:20 etc вне форума
etc
 
Аватар для etc
В рантайме.
Старый 13.07.2010 13:36 i.o. вне форума
i.o.
 
Аватар для i.o.
Объясните, пожалуйста, в чем практическая ценность этих метадат? Как в реальной практике это может помочь?
В данном примере брать css из метадаты как-то странно. В чем проблема создать дополнительное свойство класса или экземпляра с этим значением?
Старый 13.07.2010 15:39 wvxvw вне форума
wvxvw
 
Аватар для wvxvw
Проблема создавать свойство в том, что оно будет наследоваться, ну и вообще захламлять автокомплит. А практическая ценность... ну, скажем так, всегда можно обойтись без. Иногда это удобно (для порядка / наглядности / получить какую-нибудь инфу через отражение).
 

 


Часовой пояс GMT +4, время: 14:08.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.