Zvolme si, jak vhodit - MujMAC.cz - Apple, Mac OS X, Apple iPod

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kde se narodil známý fotograf František Drtikol?

V dnešní soutěži hrajeme o:

Seriály

Více seriálů



Software

Zvolme si, jak vhodit

14. prosince 2006, 00.00 | Předminule jsme se naučili základy toho, jak implementovat view jež může sloužit jako zdroj tažení; minule jsme si ukázali elementární implementaci view, jež dokáže fungovat při tažení jako cíl pro vhazování. Dnes se podíváme na nejběžnější z možných vylepšení: ukážeme si, jak modifikovat akci, již tažení provede, pomocí klávesových přepínačů.

Náš seriál, věnovaný standardnímu vývojovému prostředí Mac OS X Cocoa, se v současnosti zabývá tím, kterak zajistit podporu pro služby "drag & drop" v rámci širšího tématu, jímž se nyní zabýváme – implementací vlastních objektů view.

Předminule jsme se naučili základy toho, jak implementovat view jež může sloužit jako zdroj tažení; minule jsme si ukázali elementární implementaci view, jež dokáže fungovat při tažení jako cíl pro vhazování. Dnes se podíváme na nejběžnější z možných vylepšení: ukážeme si, jak modifikovat akci, již tažení provede, pomocí klávesových přepínačů.

Jak to funguje?

Základní princip je velmi jednoduchý: zdrojový kontrolér tažení – tedy objekt, implementující (alespoň zčásti) protokol NSDraggingSource, který určíme při volání standardní metody dragImage:at:offset:event:pasteboard:source:slideBack: – prostě nabídne více možných operací: všechny, jež v principu dokáže zpracovat. Operace jsou samozřejmě nabídnuty prostřednictvím jeho metody draggingSourceOperationMaskForLocal: (a jak víme, mohou být odlišné pro tažení lokální – tedy uvnitř jedné a téže aplikace – a pro tažení mezi dvěma různými aplikacemi).

Framework Cocoa se pak zcela automaticky postará o to, aby skupina operací, nabídnutých cílovému view, odpovídala momentální situaci při tažení:

  • není-li stisknutý žádný přepínač, nabídne se kompletní a neomezený seznam operací, jež publikoval zdroj tažení;
  • drží-li uživatel stisknutý přepínač "ctrl", je k dispozici pouze operace NSDragOperationLink (tedy vytvoření zástupce pro sdílení taženého objektu);
  • drží-li uživatel stisknutý přepínač "alt", je k dispozici pouze operace NSDragOperationCopy (tedy vytvoření kopie taženého objektu);
  • drží-li uživatel stisknutý přepínač "ctrl", je k dispozici pouze operace NSDragOperationGeneric (jež se v tomto kontextu nejčastěji – ale samozřejmě ne nutně – využívá jako ekvivalent NSDragOperationMove, tedy přemístění taženého objektu z původního místa na nové);
  • přepínače lze ovšem i libovolně kombinovat: drží-li tedy uživatel kupříkladu stisknuté přepínače "ctrl" i "alt" zároveň, budou k dispozici také obě operace, NSDragOperationLink i NSDragOperationCopy.

Zbývá již jen drobnost – vybrat si z nabídky operací (je-li jich více) požadovanou, případně rozhodnout, že z dané nabídky nelze akceptovat operaci žádnou: to již je úkolem cíle tažení. Připomeňme standardní metodu draggingEntered: z protokolu NSDraggingDestination; ta dostane seznam přípustných operací prostřednictvím argumentu (jímž je speciální objekt odpovídající protokolu NSDraggingInfo, a ten – kromě řady jiných věcí – také dokáže vrátit paletu přípustných operací). Zvolenou operaci pak vrátí; ve speciálním případě jí může být i NSDragOperationNone, což znamená "žádnou z nabízených operací nelze akceptovat".

Úprava zdroje

Pojďme si to opět ukázat v praxi. Nejprve zřejmou a jednoduchou změnu metody draggingSourceOperationMaskForLocal:, jež bude – dejme tomu – nabízet operace kopírování, přemístění (representovanou konstantou NSDragOperationGeneric), a zrušení tažených čar:

-(unsigned)draggingSourceOperationMaskForLocal:(BOOL)local {
  return
  NSDragOperationCopy|NSDragOperationGeneric|NSDragOperationDelete;
}

To ovšem samo tak docela nestačí: uvědomme si, že ve chvíli, kdy je tažení zahájeno, ještě nevíme, jakou operaci nakonec uživatel (a cílové view, k němuž se samozřejmě vrátíme za chvilku) zvolí. Proto potřebujeme zpětnou informaci o tom, jaká operace byla nakonec použita: pakliže se jednalo o operaci kopírování (nebo bylo-li tažení zrušeno bez úspěšného vhození), nemusíme dělat nic – stejně, jako tomu bylo v minulé versi aplikace. Naproti tomu ale, pokud byla zvolena operace NSDragOperationGeneric či NSDragOperationDelete, musíme čáry zrušit, stejně, jako kdyby uživatel vybral příkaz "Delete".

K tomu slouží metoda draggedImage:endedAt:operation: z protokolu NSDraggingSource; pokud ji náš kontrolér implementuje, framework odpovídající zprávu vždy automaticky pošle poté, kdy je tažení dokončeno – a operaci, již uživatel (ve spolupráci s cílovým objektem) zvolil, jí předá jako argument:

-(void)draggedImage:(NSImage*)image endedAt:(NSPoint)pt
  operation:(NSDragOperation)operation {
  if ((operation==NSDragOperationGeneric ||
       operation==NSDragOperationDelete) && [lines count]) {
    if (selection<[lines count])
      [lines removeObjectAtIndex:selection];
    else [lines removeAllObjects];
    [self setNeedsDisplay:YES];
  }
}

Obsah metody je předpokládám zcela zřejmý; snad jen za zmínku stojí, že by bylo zřejmě čistší udělat si pro rušení pomocnou metodu, jež by se volala jak odsud, tak i z metod delete:, cut: a deleteBackward:. I přes drobnou a snadno řešitelnou komplikaci s tím, že při službě "drag&drop" či "Cut" samozřejmě nechceme zobrazit "alert", kdežto při mazání ano, to ponecháme jako lehounké cvičení laskavému čtenáři :)

Úprava cíle

V implementaci protokolu NSDraggingDestination především nahradíme metodu draggingEntered: metodou draggingUpdated:. Ta funguje velmi podobně, má týž argument a stejnou návratovou hodnotu; rozdíl mezi nimi však spočívá v tom, že draggingEntered: je volána toliko jedinkrát, ve chvíli, když při tažení vstoupíme do oblasti cílového view; metoda draggingUpdated: je naproti tomu volána znovu pokaždé, když se něco změní – kupříkladu pozice myši nebo nastavení přepínačů. A právě kvůli této druhé změně tuto "vylepšenou" metodu potřebujeme: pokud se vzhledem k jiné skupině přepínačů změní skupina přípustných operací, je samozřejmě zapotřebí, abychom na ni znovu zareagovali.

V konkrétní ukázkové implementaci metody draggingUpdated: si ukážeme ještě jednu fintu: tou je zpráva draggingSource, již můžeme zaslat objektu odpovídajícímu protokolu NSDraggingInfo, jenž jsme dostali jako argument; tento objekt nám vrátí buďto zdroj tažení, je-li v téže aplikaci – nebo nil, pokud je zdroj tažení v aplikaci jiné.

My toho využijeme k implementaci víceméně standardní logiky "při tažení v téže aplikaci preferujeme přemístění, při tažení mimo aplikaci preferujeme kopii":

-(NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
  if (![[[sender draggingPasteboard] types] containsObject:PBType])
    return NSDragOperationNone;
  unsigned mask=[sender draggingSourceOperationMask];
  if ([sender draggingSource]) { // local
    if (mask&NSDragOperationGeneric) return NSDragOperationGeneric;
    if (mask&NSDragOperationCopy) return NSDragOperationCopy;
  } else { // other app
    if (mask&NSDragOperationCopy) return NSDragOperationCopy;
    if (mask&NSDragOperationGeneric) return NSDragOperationGeneric;
  }
  return NSDragOperationNone;
}

Nakonec jen mírně upravíme metodu performDragOperation:, aby dokázala korektně zpracovat obě operace, kopii a přemístění – uvědomme si, že z hlediska cílového view mezi oběma operacemi není rozdílu, ty se liší pouze z pohledu zdrojového kontroléru (a v něm již jsme odpovídající logiku implementovali):

-(BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
  NSPasteboard *pboard=[sender draggingPasteboard];
  if (![[pboard types] containsObject:PBType] ||
      !([sender draggingSourceOperationMask]&
        (NSDragOperationCopy|NSDragOperationGeneric)))
    return NO;
  return [self pasteFromPasteboard:pboard];
}

A co delete?

Kde je využití operace NSDragOperationDelete, pravíte? Ale to je jednoduché – zkuste odtáhnout čáry z naší aplikace do doku do standardního koše :)

Bohužel, jedna malá chybka tu je: dok není poctivá kakaová aplikace, nýbrž carbonovská čuňačina, a jako taková celkem předpokládatelně nepodporuje protokoly zcela korektně. Zde je problém konkrétně v tom, že dok by nám pokojně vrátil operaci NSDragOperationDelete dokonce i v případě, že bychom ji z metody draggingSourceOperationMaskForLocal: vůbec nevrátili! Pracujeme-li tedy se složitější a dynamicky generovanou sadou operací, je třeba si na to dát pozor.

Není to tak docela ono...

V podstatě jsme plus mínus hotovi, aplikace v zásadě funguje... několik chyb v ní ale zůstalo. Zdalipak je dokážete najít a odstranit? Pokud ne, příště se na ně podíváme podrobněji...

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » Informace  

 » Rubriky  » Agregator  

 » Rubriky  » Tipy a Triky  

 » Rubriky  » Začínáme s  

 » Rubriky  » Software  

Diskuse k článku

 

Vložit nový příspěvek   Sbalit příspěvky

 

Zatím nebyl uložen žádný příspěvek, buďte první.

 

 

Vložit nový příspěvek

Jméno:

Pohlaví:

,

E-mail:

Předmět:

Příspěvek:

 

Kontrola:

Do spodního pole opište z obrázku 5 znaků:

Kód pro ověření

 

 

 

 

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: