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

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

Рейтинг: 5.00. Голосов: 3.

Менеджер курсоров на базе стека

Запись от expl размещена 21.01.2012 в 03:51
Обновил(-а) expl 21.01.2012 в 21:03

О чем это всё?

Это описание подхода к управлению курсорами, который применяю около 2-х лет на разных проектах

Зачем это читать?
Может это поможет Вам выкинуть сотни строк чудовищной логики из своего проекта.
А может и не поможет - просто интересно, использовал кто нибудь такие подходы или какие применял альтернативы (больше из любопытства, т.к. описываемый метод меня полностью устраивает)

Итак задача:
Написать ActionScript 3 класс(ы) для отображения курсоров, нарисованных художниками (или полученных другим способом) для использования в проекте. И хочется, чтобы это использование было полегче и проблем создавало поменьше.

Критика наиболее распространнённого, "наивного" подхода
(если его не покритиковать - то непонятно, зачем надо было "изобретать велосипед")


Обычно делается менеджер с одним единственным публиным полем, и смена курсора происходит так:
Код AS3:
	CursorManager.cursor = myCursor;
А выключение курсора как-то так:
Код AS3:
CursorManager.cursor = null;
Первое что бросается в глаза - глобальность. Т.е. отдельные подсистемы приложения будут мешать друг другу устанавливать курсоры.

Но начнём с более простого примера:
1. Есть кнопка, лежащая в окне
Код:
              -------        button
        ------------------ window
2. Надо сделать, чтобы при наведении на кнопку показывался курсор "нажать" (нарисованный художниками)
3. А при вождении по окну показывался курсор "перетащить окно" (тоже нарисованный художниками)

Как это делается:
Код AS3:
window.addEventListener(MouseEvent.ROLL_OVER, onWindowRollOver);
window.addEventListener(MouseEvent.ROLL_OUT, onWindowRollOut);
private function onWindowRollOver(event:MouseEvent):void
{
	// Условно, чтобы не засорять статью,
	// там может быть что-то типа cursor = new Cursor(new Bmp_move_window());
	CursorManager.cursor = "перетащить окно";
}
private function onWindowRollOut(event:MouseEvent):void
{
	CursorManager.cursor = null;
}
button.addEventListener(MouseEvent.ROLL_OVER, onButtonRollOver);
button.addEventListener(MouseEvent.ROLL_OUT, onButtonRollOut);
private function onButtonRollOver(event:MouseEvent):void
{
	CursorManager.cursor = "нажать";
}
private function onButtonRollOut(event:MouseEvent):void
{
	CursorManager.cursor = null;
}
Так вот, это _не_ работает . Вернее работает, но _очень_ бажно:
- наводим мышку на окно - появляется курсор "перетащить окно";
- переводим мышку на кнопку - появляется курсор "нажать" (пока все идёт нормально)
- переводим мышку с кнопки на окно - курсор исчезает (опаньки!)
Это происходит потому, что кнопка лежит в окне и при уводе курсора с кнопки, события наведения на окно не происходит.

Чешем репу. Наверно, надо при наведении на кнопку запоминать последний курсор в менеджере и при
уводе с кнопки возвращать его:

Код AS3:
private var _lastCursor:String;
private function onButtonRollOver(event:MouseEvent):void
{
	_lastCursor = CursorManager.cursor;
	CursorManager.cursor = "нажать";
}
private function onButtonRollOut(event:MouseEvent):void
{
	CursorManager.cursor = _lastCursor;
}
Увеличиваем вложенность до 3-х:

Код:
--------------------------------------------------> перемещение мыши
--------------------------------------------------> время

           3-------4        button("cursor3")
       2------------------5 window("cursor2")
  1------------------------------------6 container ("cursor1")

1. [container OVER] cursor = "cursor1"
2. [window OVER] cursor = "cursor2", window._lastCursor = "cursor1",
3. [button OVER] cursor = "cursor3", button._lastCursor = "cursor2",
4. [button OUT] cursor = (button._lastCursor == "cursor2"),
5. [window OUT] cursor = (window._lastCursor == "cursor1"),
6. [container OUT] cursor = null;
Работает!
Но хранить кучу ссылок на каждом элементе и следить за ними!?
И так ведь будет хватать другой, нужной, логики...
Может засунуть _lastCursor в менеджер? Не, не получится - будет бажить.

Идем дальше. Обнаруживается, что раз в 30 секунд идет перевтягивание данных окна (это для примера, я так не делаю). Во время перевтягивания надо показывать курсор с песочными часиками (не зависимо от того, где находится курсор).
А вот здесь уже не работает
Код:
------------------------------------------------------> перемещение мыши
--------------X     waiting("cursor2") X--------------> время
              |                        |
           1--2----3                   4   button("cursor1")
	  
1. [button OVER] cursor = "cursor1", button._lastCursor = null;
2. [waiting on] cursor = "cursor2", waiting._lastCursor = "cursor1";
3. [button OUT] cursor = (button._lastCursor == null), (Мимо! - должен остаться "cursor2")
4. [waiting off] cursor = (waiting._lastCursor == "cursor1"), (Мимо! - должен быть null)
Но это гипотетические варианты. А теперь представим простенькую социалочку.
Обычно в большинстве социалок существует редактирование карты,
когда курсоры меняются в зависимости:
  • от того, на какой объект наведена мышка
  • от того, какой режим включен (поливка, убирание сорняков, сбор урожая)
  • от того, в какой стадии редактирования вы находитесь (выбрали начало дороги - один курсор, начали проводить дорогу - другой)
  • в любой момент может появится поп-ап, при наведении на который курсора быть не должно или должен быть свой курсор
  • при наведении на кнопки этого поп-апа должны быть свои курсоры тоже.
Причём все эти аспекты надо учитывать одновременно.

Чтож, здесь баловаться запоминанием курсоров опасно. Поэтому пишем менеджер с чудесной логикой, знающий о половине классов игры.
Это что, действительно стандартный и нормальный подход?

Теперь критики достаточно, переходим к теме топика.

Принципы работы менеджера на базе стека

Вспомним, как мы хотели упростить логику с запоминаниями:
Может засунуть _lastCursor в менеджер? - не получится, будет бажить.

Одной переменной не получится, а стеком - получится!
И пусть каждая подсистема возьмет себе маленький переключатель и будет включать его и выключать, добавляя и убирая себя из стека.
Все! В этом весь смысл.

Но мы не только упростим таким образом систему "запоминаний" для клиента.
Бонусом мы получим простое решение всех вышеперечисленных проблем.

Теперь разберёмся, как это выглядит в коде.

Та же задача:
Код:
------------------------------------------------------> перемещение мыши
--------------X     waiting("cursor2") X--------------> время
              |                        |
           1--2----3                   4   button("cursor1")
Код:
Код AS3:
// "cursor1" - условно, ниже будет приведён пример, который компилируется
_buttonSwitcher = CursorManager.newSwitcher().setCursor("cursor1");
button.addEventListener(MouseEvent.ROLL_OVER, onButtonRollOver);
button.addEventListener(MouseEvent.ROLL_OUT, onButtonRollOut);
private function onButtonRollOver(event:MouseEvent):void
{
	_buttonSwitcher.enabled = true;
}
private function onButtonRollOut(event:MouseEvent):void
{
	_buttonSwitcher.enabled = false;
}
...
// Где-то в далёком классе, ведающем включением анимации ожидания
_waitingSwitcher = CursorManager.newSwitcher().setCursor("cursor2");
private function startLoading():void
{
	_waitingSwitcher.enabled = true;
	load(onLoadComplete, onLoadError);
}
private function onLoadComplete():void
{
	_waitingSwitcher.enabled = false;
}
private function onLoadError():void
{
	// Ошибка - не повод оставлять курсор ожидания
	_waitingSwitcher.enabled = false;
}
Примечание:
На самом деле даже не обязательно вешать слушатели onButtonRollOver и onButtonRollOut на кнопку, можно сделать тоже самое одной строчкой с помощью HoverSwitcher, но суть не в этом (и для наивного подхода можно было написать класс, навешивающий слушатели).

Как это работает?
Переключатели, у которых вызвали enabled = true добавляем наверх стека, а переключатели, у которых вызвали enabled = false удаляем откуда придется. И выбираем курсор, который принадлежит верхнему переключателю. Всё!
Код:
------------------------------------------------------> перемещение мыши
--------------X     waiting("cursor2") X--------------> время
              |                        |
           1--2----3                   4   button("cursor1")

1. [button OVER] stack = [buttonSwitcher], cursor = (buttonSwitcher.cursor == "cursor1")
2. [waiting on] stack = [buttonSwitcher, waitingSwitcher], cursor = (waitingSwitcher.cursor == "cursor2")
3. [button OUT] stack = [waitingSwitcher], cursor = (waitingSwitcher.cursor == "cursor2")
4. [waiting off] stack = [], cursor = null
Всё правильно!

Зачем нужен ещё приоритет?
- вы включили курсор ожидания;
- пользователь навел мышку на кнопку;
- вместо курсора ожидания появился курсор мыши;
- польозватель убрал мышку с кнопки;
- появился курсор ожидания.
Впринципе, с наивным вариантом у нас бы и он не появился, но, согласитесь неприятно

Решается просто - выставляем переключателю анимации ожидания приоритет 1.
Т.е. говорим, этот переключатель главнее других (по умолчанию 0)
Код AS3:
_waitingSwitcher = CursorManager.newSwitcher(1).setCursor("cursor2");
Все, никто не перебьет этот курсор (только переключатели с более старшими приоритетами), пока его не выключит владелец _waitingSwitcher.

Но в большинстве случаев можно поставить куче переключателей одинаковый приоритет, пусть даже 0.

И, собственно, пример (использует minimalcomps, сам CursorManager приложен в архиве,
если лень компилировать - в архиве же лежит swf-ка):

Код AS3:
private function init(e:Event = null):void 
{
	removeEventListener(Event.ADDED_TO_STAGE, init);
	// entry point
 
	// Инициализация курсоров
	var defaultCursor:Cursor = new Cursor(
		new Label(null, 0, 0, "Default cursor"), 10, 14, true);
	var cursor0:Cursor = new Cursor(
		new Label(null, 0, 0, "Cursor 0"), 10, 14, true);
	var cursor1:Cursor = new Cursor(
		new Label(null, 0, 0, "Cursor 1"), 10, 14, true);
	var waitCursor:Cursor = new Cursor(
		new Label(null, 0, 0, "Please wait..."), 0, 0, true, true);
 
	// Установка дефолтного курсора
	CursorManager.init(stage, stage);
	CursorManager.defaultCursor = defaultCursor;
 
	// Добавление курсора на кнопку
	var button0:PushButton = new PushButton(this, 0, 0, "Button 0");
	CursorManager.newHover().setTarget(button0).setCursor(cursor0);
 
	// Кнопка с блокировкой курсоров
	var buttonWithCursorLock:PushButton = new PushButton(
		this, 0, 30, "Button with cursor lock");
	buttonWithCursorLock.width = 120;
	CursorManager.newHover()
		.setTarget(buttonWithCursorLock).setCursor(null);
 
	// Кнопка без установки курсора
	new PushButton(this, 0, 60, "Button without cursor").width = 120;
 
	// Кнопки в окне
	var window:Window = new Window(
		this, 0, 200, "Window without cursor");
	window.width = 120;
	CursorManager.newHover().setTarget(window).setCursor(null);
	var button1:PushButton = new PushButton(
		window.content, 10, 10, "Button 1");
	CursorManager.newHover().setTarget(button1).setCursor(cursor1);
	var windowButtonWithoutCursor:PushButton = new PushButton(
		window.content, 0, 50, "Button without cursor");
	windowButtonWithoutCursor.width = 120;
	window.content.addChild(windowButtonWithoutCursor);
 
	// Чек-бокс включения курсора ожидания, затмевающего остальные
	_waitSwitcher = CursorManager.newSwitcher(1).setCursor(waitCursor);
	_waitCheckBox = new CheckBox(this, 130, 0, "Enable wait");
	_waitCheckBox.addEventListener(MouseEvent.CLICK, onWaitClick);
 
	// Изменение курсора кнопки
	var randomButton:PushButton = new PushButton(
		this, 130, 30, "Click for random cursor", onSetRandomClick);
	randomButton.width = 120;
	randomButton.addEventListener(MouseEvent.CLICK, onSetRandomClick);
	_randomSwitcher = CursorManager.newHover().setTarget(randomButton);
}
 
private var _waitCheckBox:CheckBox;
private var _waitSwitcher:Switcher;
 
private function onWaitClick(event:MouseEvent):void
{
	_waitSwitcher.enabled = _waitCheckBox.selected;
}
 
private var _randomSwitcher:Switcher;
 
private function onSetRandomClick(event:MouseEvent):void
{
	_randomSwitcher.cursor = new Cursor(
		new Label(null, 0, 0, "Random cursor: " + Math.random()),
		10, 14, true);
}
[color="DarkOrange"]Итого:[COLOR] Чтобы установить курсор, не надо знать чем занимаются другие части приложения.

Спасибо что дочитали до этого места! Надеюсь, это было не очень скучно.

Архив с примером, исходниками менеджера и swf-кой примера AS3CursorManager.zip

P.S.
Вообще, раза 2 за всю историю, этот подход использовался для других вещей (не касающихся курсоров). Применялся в случаях, когда компонент может отобразить только одно значение, а понять чье именно надо отобразить - непросто. Но эти ситуации крайне редки - ведь если надо отобразить данные 10-ти компонентов, то все 10 надо показывать, а не выбирать что-то одно.
Всего комментариев 16

Комментарии

Старый 21.01.2012 05:46 fljot вне форума
fljot
А почему не сделать проще? Вместо заморочек с newSwitcher(1):
Код AS3:
CursorManager.add(cursor, priority);
CursorManager.remove(cursor, priority);
CursorManager.removeAll();
Плюс убрать лишнее типа newHover (это раньше были только MouseEvent, теперь же появляются TouchEvent плюс куча кастомных вроде как у Starling'а, TUIO библиотек и проч). Да, синтаксический сахар, да, удобно может быть. Но лучше это написать немного отдельно, типа как дополнение.

Кстати, во Flex'е как-то так и реализовано (по крайней мере было, если не ошибаюсь).
Старый 21.01.2012 14:36 expl вне форума
expl
Вроде-бы можно и так.
Просто один и тот же курсор может добавляться несколькими подсистемами, поэтому обернул в Switcher. Плюс там поле cursor у Switcher можно менять и если он на вершине стека, то курсор будет меняться.

Цитата:
Плюс убрать лишнее типа newHover (это раньше были только MouseEvent, теперь же появляются TouchEvent плюс куча кастомных вроде как у Starling'а, TUIO библиотек и проч)
Ну убрать, не убрать newHower - это на любителя. Используется весьма часто, поэтому оставил.

Цитата:
Кстати, во Flex'е как-то так и реализовано (по крайней мере было, если не ошибаюсь).
Вот во флексе не смотрел. Смотрел в коде коллег и в AsWing - сильно огорчился и решил свой "велосипед" выложить. Похоже он действительно оказался "велосипедом".
Старый 21.01.2012 15:17 Tails вне форума
Tails
 
Аватар для Tails
Использовать систему приоритета для курсора действительно гораздо удобнее, чем менять его в зависимости от событий.
Старый 21.01.2012 18:31 Inet_PC вне форума
Inet_PC
 
Аватар для Inet_PC
Цитата:
button.addEventListener(MouseEvent.ROLL_OVER, onButtonRollOut);
Видимо опечатка и копи-паст в других местах, поправьте.
Получается если я в одном месте показал курсор с приоритетом 3, а в другом хочу показать с приоритетом 4 то мне самому нужно помнить о приоритете? Метода получения текущего приоритета не вижу. Может стоит добавить метод типа setTopCursor или, что-то типа этого. Можно пояснить в чем разница между newSwitcher и newHover? Ну и плюс, в каждом конкретном проекте, логика может, хоть чуток, да отличаться, а занаследоваться от статического класса я не могу (чтобы подправить его под себя). Получается не очень удобно.
Обновил(-а) Inet_PC 21.01.2012 в 18:51
Старый 21.01.2012 21:40 expl вне форума
expl
Спасибо, поправил.
По поводу приоритетов, 2-х летняя практика применения такая (не подводила):
- в половине подсистем просто присваивается приоритет 0 - стек справляется без различий в приоритетах
- для другой половины делается перечисление (одно на всё приложение):
Код AS3:
public class CursorPriority
{
    /** приоритет курсора, блокирующего курсоры над окнами */
    public static const WINDOW_LOCK_CURSOR:int = 1;
 
    public static const WINDOW_BUTTON:int = 2;// Должен быть выше чем у блокирующего курсоры при наведении на окна
 
    /** курсор с песочными часами */
    public static const WAIT:int = 100;
 
    /** инстурменты на карте при редактировании */
    public static const TOOL:int = 0; // Этот приоритет использует куча курсоров
}
А отдельные классы используют это перечисление для установки своих курсоров.

Цитата:
Может стоит добавить метод типа setTopCursor
Потом появятся методы "добавить курсор с самым низким приоритетом" и другие - только путаница добавится, проще использовать глобальное перечисление приоритетов

Цитата:
Можно пояснить в чем разница между newSwitcher и newHover?
newHover навешивает слушатели ROLL_OVER и ROLL_OUT на объект (если Вам надо навесить курсор над визуальным объектом), при newSwitcher - это вам придется делать вручную.
Но, например при переключении инструментов вам надо переключать курсор в ручную, соответственно слушатели не нужны, поэтому надо использовать newSwitcher.
Т.е. newHover сделан для удобства - не более.

Цитата:
Ну и плюс, в каждом конкретном проекте, логика может, хоть чуток, да отличаться, а занаследоваться от статического класса я не могу (чтобы подправить его под себя)
За 2 года менеджер (мы используем немного другой код, но суть та же, по понятным причинам я не могу его привести) использовался в проектах напрямую без каких либо оберток и не претерпевал изменений.

Но никто не запрещает Вам постирать static-и и использовать в виде объекта, наследовать и т.п.
Здесь, например, я не стал делать менеджер CCursorManager статическим, просто сделал для него статическую обертку MCursorManger по-дефолту, чтобы кому надо - наследовали и переопределяли, а кому не надо - не мучались.

Вообще, да, я огребал с поддержкой статических классов (но не с этим ), здесь просто побоялся усложнить код, т.е. главное в было донести суть подхода.
Обновил(-а) expl 21.01.2012 в 21:53
Старый 22.01.2012 22:09 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
@expl, офигительная идея, понравилось =) Единственное - в данном случае, как я понимаю, Switcher является своего рода "замкОм", по которому идентифицируется позиция в стеке. Если я все правильно понял, то можно обойтись без него, в качестве "замка" передавать сам объект, вызывающий смену курсора. То есть минус одна сущность. Однако, это имеет смысл только если всегда выполняется условие "один объект - один Switcher". Твоя идея позволяет на один объект иметь несколько свитчеров, и это круто. В общем, спасибо за идею =)
Старый 23.01.2012 16:33 crazyone вне форума
crazyone
 
Аватар для crazyone
Я делал по разному, но в итоге остановился на подходе, при котором курсор является внешним свойством интерактивного объекта. Внешним в том смысле, что хранится оно в менеджере курсоров. Типа так:
Код AS3:
Cursor.register(displayObject,cursorType,stateFlags);
А вся логика переключения между курсорами при этом инкапсулирована в менеджере и основывается на состоянии приложения, т.е. - кто находится под курсором, зажата ли та или иная кнопка, включено ли состояние ожидания и т.д.
Потому что если эту логику размазать по приложению, позволяя разным объектам принимать решение - какой курсор показать - рано или поздно будет месиво.
Старый 23.01.2012 17:27 expl вне форума
expl
Т.е. у вас Cursor - это по сути контроллер, завязанный на приложение?
Впринципе я верю, что с такой логикой можно совладать, если этому менеджеру компоненты системы расказывают в каком они состоянии, а не Cursor следит сам за всем что происходит в приложении.

Данных о себе системы передают не много, похоже они все в stateFlags умещаются. Я прав?

Цитата:
Потому что если эту логику размазать по приложению, позволяя разным объектам принимать решение - какой курсор показать - рано или поздно будет месиво.
Не поверите - месиво не наступает. Вся логика, которую вы реализуете в менеджере у меня сводилась к стеку и приоритетам. Ну не попадалось таких проектов, на которых требовалось делать что-то большее.

P.S. Сам после написания пары отвратительных менеджеров, которые были осведомлены о половине приложения, перешел на стековую систему и более ничего не испытывал. Может подход со stateFlags тоже работает, но зачем нам писать еще менеджер, зависящий от игры.
Обновил(-а) expl 23.01.2012 в 17:31
Старый 23.01.2012 21:32 crazyone вне форума
crazyone
 
Аватар для crazyone
Да нет, он на приложение не завязан. Идея примерно в том же - мы регистрируем нужный курсор для объектов, только без создания переключателей. В stateFlags всего лишь информация о состоянии - например, Cursor.CTRL_PRESSED|Cursor.SHIFT_PRESSED (работает так же, как флаги в Array.sort)

Цитата:
Не поверите - месиво не наступает. Вся логика, которую вы реализуете в менеджере у меня сводилась к стеку и приоритетам. Ну не попадалось таких проектов, на которых требовалось делать что-то большее.
Да, я присмотрелся к коду - логика управления примерно такая же - установили курсор объекту, поменяли его или убрали... Только у меня не попадалось надобности назначать курсорам еще и приоритеты, а идею со стеком я не совсем понял =)
Старый 23.01.2012 21:56 dimarik вне форума
dimarik
 
Аватар для dimarik
Я джва года ждал этого менеджера.
Старый 23.01.2012 22:34 expl вне форума
expl
А я два года думал публиковать или не стоит

Цитата:
Да нет, он на приложение не завязан. Идея примерно в том же - мы регистрируем нужный курсор для объектов, только без создания переключателей. В stateFlags всего лишь информация о состоянии - например, Cursor.CTRL_PRESSED|Cursor.SHIFT_PRESSED (работает так же, как флаги в Array.sort)

Цитата:
Не поверите - месиво не наступает. Вся логика, которую вы реализуете в менеджере у меня сводилась к стеку и приоритетам. Ну не попадалось таких проектов, на которых требовалось делать что-то большее.
Да, я присмотрелся к коду - логика управления примерно такая же - установили курсор объекту, поменяли его или убрали... Только у меня не попадалось надобности назначать курсорам еще и приоритеты, а идею со стеком я не совсем понял =)
Примерно понял как Вы организовали, у меня даже посложнее получается, но и возможностей поболе.

А со стеком - проще всего понять если взять все курсоры с приоритетом 0 и назначить их отдельным вложенным друг в друга объектам. Там автоматом над внутренними объектами будет показываться их курсор, а при переводе мышки на более внешние - курсор внешних.
Т.е. при установке нового курсора - показывается он, при убирании - последний добавленный активный(к тому моменту некоторые могут быть уже убраны из стека - деактивированы) - это удобно и при ручной установке курсоров.

Да и у Вас ведь, когда добавляете
Код AS3:
Cursor.register(displayObject,cursorType,stateFlags);
курсор - он же в каком-нибудь массиве или другой структуре хранится вместе с другими и вы как-то выбираете из этой структуры какой именно отображать. Только Вы выбираете из этой структуры на основе какой-то своей логики, а я просто беру верхний с наибольшим приоритетом.
Обновил(-а) expl 23.01.2012 в 22:49
Старый 23.01.2012 23:24 carrotoff вне форума
carrotoff
 
Аватар для carrotoff
Цитата:
Сообщение от expl
А я два года думал публиковать или не стоит
Всё-таки, правильно джва.
пруф
Старый 23.01.2012 23:40 mayakwd вне форума
mayakwd
 
Аватар для mayakwd
«Дайте я вас расцелую» (c)
Старый 25.01.2012 00:32 fish_r вне форума
fish_r
 
Аватар для fish_r
Идея понравилась. Реализация... странная ), интересна логика неисповедимые пути которой привели вас именно к такой конструкции ).

Будут проблемы с удалением объекта - менеджер не предусматривает хранения ссылок и ликвидации HoverSwitcher-ов, а те, в свою очередь регистрируют слушателей в объектах ссылку на который получили в аргументах вызова, пока слушатели не будут отписаны объект будет находиться в памяти. Выход: хранить ссылку на свитчер где-то "снаружи", что конечно не кошерно...
Старый 25.01.2012 15:44 expl вне форума
expl
Цитата:
Будут проблемы с удалением объекта - менеджер не предусматривает хранения ссылок и ликвидации HoverSwitcher-ов, а те, в свою очередь регистрируют слушателей в объектах ссылку на который получили в аргументах вызова, пока слушатели не будут отписаны объект будет находиться в памяти.
1. Во-первых в памяти застревать они не будут.
Следим внимательно:
- создается HoverSwitcher, если его никакому полю не присвоили - на него ссылается только target через подписку(благодаря этой подписке его не сносит GC)
- при активации курсора ссылка на Switcher попадает в CursorManager, при деактивации - исчезает
- убрали target со сцены, обнулили ссылки - если курсор был неактивен - на switcher не осталось ссылок и его спокойно собирает GC (ведь target становится подписанным на объект, который никто не мешает GC удалить из памяти).

Все время ожидания срабатывания GC на target навешаны слушатели ROLL_OVER и ROLL_OUT - они никогда не сработают для объекта, не находящегося в списке отображения - значит ресурсов в ожидании удаления тоже жрать этот switcher не будет.

Может ли оказаться так, что сносят target с наведенной на него мышкой и активным курсором без срабатывания события roll_out (т.е. можно ли снести target с активным курсором)? - сомневаюсь.
2. Во-вторых. Механизм отписки есть - достаточно присвоить HoverSwitcher::target = null.

Проблемы действительно могут возникнуть, если вы навешиваете несколько переключателей на один target - тогда надо сохранять ссылку на предыдущий switcher, присваивать target = null (там в сеттере отписка) и вешать новый. (или без навески нового свитчера - если курсоры боле не нужны)

Цитата:
Выход: хранить ссылку на свитчер где-то "снаружи", что конечно не кошерно...
Это что ни на есть кошерно и правильно - каждой системе раздать по объекту, которым они управляют, а не регистрировать все в одном центре (централизация, конечно рулит, но децентрализация рулит еще больше)

Просто кому Вы предлагаете заниматся отпиской, если не switcher-у?

И, если нужно менять курсор target-а, то ссылку на switcher все равно надо где-то сохранить.

P.S. В конце концов, можно вообще игнорировать этот HoverSwitcher и навешивать слушатели самому - кроме увеличения объемов кода проблем у вас не будет. HoverSwitcher - это чисто вспомогательный функционал. (хотя простой switcher вам придется хранить в каком-нибудь поле)
Обновил(-а) expl 25.01.2012 в 15:59
Старый 03.02.2014 18:15 Akopalipsis вне форума
Akopalipsis
expl большое Вам спасибо за менеджер, но я немного не понимаю принцип с приоритетами. Если Вас не затруднит, не могли бы ответить в этой теме.
 

 


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


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