Byli jsme na tahu; nyní padneme. - 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

 

Jaký fotograf/ka získal/a cenu za nejpopulárnější příspěvek v Nikon Photo Contest?

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

Seriály

Více seriálů



Informace

Byli jsme na tahu; nyní padneme.

8. prosince 2006, 00.00 | Minule jsme si ukázali základní implementaci view, jež dokáže fungovat jako zdroj tažení. Dnes se naopak podíváme na to, kterak zpracovat tažení objektů, jež inicioval někdo jiný: umožníme našemu view, aby bylo cílem vhození.

V seriálu, věnovaném programování ve vývojovém prostředí Cocoa v operačním systému Mac OS X, se v poslední době zabýváme programováním views – našich vlastních dědiců třídy NSView. Každé view má nějaký vzhled – způsobem, kterak jej naprogramovat s využitím standardní metody drawRect:, jsme se zabývali hned zpočátku –, a dokáže zpracovávat nejrůznější "události", generované uživatelem. Ukázali jsme si již práci s myší i s klávesnicí, a nyní se zabýváme speciálními službami souvisejícími s myší – podporou pro službu "drag&drop".

Minule jsme si ukázali základní implementaci view, jež dokáže fungovat jako zdroj tažení: víme, že v zásadě postačí implementovat objekt – často to bývá samo view, není to však nutné – který tažení řídí, a který odpovídá protokolu NSDraggingSource. Kromě toho pak již jen stačí ve vhodný okamžik (nejspíše v rámci implementace metody mouseDragged:) poslat našemu view standardní zprávu dragImage:at:offset:event:pasteboard:source:slideBack:.

Dnes se naopak podíváme na to, kterak zpracovat tažení objektů, jež inicioval někdo jiný: umožníme našemu view, aby bylo cílem vhození.

Registrace

Prvním nutným krokem, který musíme učinit, je registrace našeho view jako příjemce "tažení" s daty daného typu: to proto, aby framework nemusel pro každé view a každý datový typ znovu a znovu ověřovat, zda je "vhození" vůbec možné. Pro neregistrovaná views se s vhazováním vůbec nepočítá.

Registrace je jednoduchá – stačí požadovanému view poslat standardní zprávu registerForDraggedTypes:, jejímž argumentem je pole obsahující identifikátory všech datových typů, jež lze do daného view vhazovat. Registrace musí proběhnout kdykoli před prvním pokusem o "drag & drop"; nejlepší místo pro ni tedy je buďto v metodě init (resp. v případě view samozřejmě initWithFrame:), nebo u objektů, načtených z NIBů, v metodě awakeFromNib.

My již v současnosti používáme metodu initWithFrame: pro přípravu pole lines; stačí do ní tedy registraci jednoduše přidat:

-initWithFrame:(NSRect)frame {
  if (!(self=[super initWithFrame:frame]))
    return nil;
  lines=[[NSMutableArray alloc] init];
  [self registerForDraggedTypes:
    [NSArray arrayWithObject:PBType]];
  return self;
}

Protokol NSDraggingDestination

Samotná registrace však nestačí: podobně, jako při implementaci zdroje tažení bylo zapotřebí implementovat alespoň jednu službu z protokolu NSDraggingSource a ještě k tomu ve vhodný okamžik vyvolat metodu dragImage:at:offset:event:pasteboard:source:slideBack:, musíme nyní vedle registrace také implementovat alespoň základy protokolu NSDraggingDestination.

Pro minimalistickou implementaci zde potřebujeme metody dvě: prvá z nich, metoda draggingEntered:, je automaticky vyvolána ve chvíli, kdy tažený objekt překročí hranice našeho view, a jejím účelem je vůbec umožnit vhození (jinými slovy, musíme splnit obě podmínky najednou: pouze objekty view, jež byly pro přijetí tažených objektů korektně zaregistrovány – jak jsme si ukázali v minulém odstavci – dostanou zprávu draggingEntered:; jen objekty, jež dostaly zprávu draggingEntered: a odpověděly kladně, mohou přijmout vhozený objekt).

Druhou z metod, jež je zapotřebí implementovat, je metoda performDragOperation:; tu framework vyvolá automaticky ve chvíli, kdy došlo k vhození objektu do našeho view (což samozřejmě může nastat pouze v případě, že to předcházející zpráva draggingEntered: "dovolila"). Jejím obsahem pak je faktické zpracování vhozeného objektu.

Argumentem obou metod je objekt, implementující protokol NSDraggingInfo; než si ukážeme možnou implementaci obou metody z protokolu NSDraggingDestination, řekneme si o tomto novém protokolu malinko více.

Protokol NSDraggingInfo

Kdykoli zahájíme tažení (s využitím prostředků, jež jsme si ukázali v minulém dílu našeho seriálu), knihovny Cocoa automaticky vytvoří objekt, odpovídající protokolu NSDraggingInfo, který representuje konkrétní proces tažení. Jeho prostřednictvím jsou ostatním spolupracujícím objektům přístupné všechny potřebné informace, týkající se tažení; mezi ty nejdůležitější patří

  • draggingSource: objekt odpovídající protokolu NSDraggingSource, který řídí tažení – v našem případě tedy půjde o samo view, obecně by se mohlo jednat o nějaké kontrolér;
  • draggingSourceOperationMask: bitové pole, obsahující všechny operace, jež jsou pro tuto konkrétní operaci tažení v současném okamžiku k dispozici. Operace jsou určeny především kontrolérem řídícím tažení (jak jsme si ukázali minule), a také mohou být modifikovány pomocí standardních kombinací přepínačů; tyto "finty" si podrobněji ukážeme příště;
  • draggingPasteboard: schránka, jež obsahuje tažená data – ačkoli za normálních okolností to vždy bude standardní systémová schránka jménem NSDragPboard, s níž jsme se také seznámili minule, nelze na to spoléhat, a kdykoli chceme se schránkou něco dělat, je zapotřebí ji získat právě touto službou.

Základní implementace

Ukažme si nejjednodušší implementaci obou metod, s nimiž jsme se seznámili v předminulém odstavci; není na ní skutečně nic těžkého, a spolu s registrací, již jsme si ukázali o několik odstavců dříve, umožní našemu view tažené objekty stejně dobře přijímat jako vysílat.

Prvá z metod určuje, zda vůbec lze tažený objekt do našeho view vhodit:

-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
  if (![[[sender draggingPasteboard] types]
    containsObject:PBType] ||
      !([sender draggingSourceOperationMask]&NSDragOperationCopy))
      return NSDragOperationNone;
  return NSDragOperationCopy;
}

Její obsah je velmi jednoduchý: pokud by snad schránka neobsahovala data našeho speciálního typu – tento test bychom si ovšem mohli v zásadě odpustit, neboť díky registraci právě jen pro tento typ se jinak metoda vůbec volat nebude; ukazujeme tedy spíše typickou implementaci –, nebo pokud by maska přípustných operací neobsahovala možnost kopírování, jakoukoli operaci zakážeme tím, že vrátíme hodnotu NSDragOperationNone.

Je-li naopak vše v pořádku, informujeme framework vrácenou hodnotou NSDragOperationCopy že lze vhodit objekt do view, a že bude vytvořena jeho kopie.

Metoda performDragOperation: pak danou akci při skutečném vhození objektu doopravdy provede:

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

Její obsah je triviální: nejprve pro jistotu znovu ověříme, zda je vše v pořádku – a ano-li, načteme obsah schránky pomocí metody pasteFromPasteboard:, již jsme si pro tento účel připravili minule. To je vše.

Spuštění dvou kopií aplikace...

Nakonec jen malá "finta": pro pohodlné a efektivní vyzkoušení naší služby "drag&drop" bychom potřebovali spustit zároveň dvě kopie naší aplikace. Bohužel, Mac OS X nahradil skvělý Workspace Manager (jenž kromě mnoha jiných podporoval i tuto službu) zhola nepoužitelnou parodií na správce souborů Finder, a v něm dokonce ani taková trivialita, jako dvojí spuštění jednoho programu, není možná; musíme si tedy pomoci jinak.

Asi nejjednodušší variantou je využít služby Terminálu: v něm stačí zapsat cestu k "executable" aplikace – ten je vždy uložen uvnitř složky "Contents/MacOS" – a aplikace se bez sebemenších problémů spustí podruhé (potřetí, podesáté... podle potřeby):

42 /tmp> ~/Generated/Debug/80_PrimitiveDrawing.app/Contents/MacOS/80_PrimitiveDrawing 

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  

 

 

 

 

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

Uživatelské jméno:

Heslo: