Táhni a padni - 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ů



Informace

Táhni a padni

6. prosince 2006, 00.00 | Na žádost v diskusi pod minulým článkem v našem seriálu o programování ve vývojovém prostředí Cocoa se dnes a v několika následujících dílech podíváme na to, jak vypadá standardní podpora technologie drag & drop.

Na žádost v diskusi pod minulým článkem v našem seriálu o programování ve vývojovém prostředí Cocoa se dnes a v několika následujících dílech podíváme na to, jak vypadá standardní podpora technologie drag & drop. Docela se to hodí, neboť základní služby jsou samozřejmě implementovány právě na úrovni třídy NSView, a právě vytvářet vlastní následníky této třídy se v poslední době učíme.

Základy

Základní princip programové podpory pro "tahání" objektů z jednoho místa na druhé pomocí myši je poměrně jednoduchý a založený na některých standardních službách třídy NSView a na trojici pomocných protokolů:

  • neformální protokol NSDraggingSource obsahuje služby, jež můžeme implementovat ve view, z nějž lze nějaký objekt myší "vytáhnout": v jejich implementaci určíme, za jakých podmínek má view na tažení myší reagovat, jakou ikonou má být tažený objekt representován, a podobně;
  • na "druhé straně" podobně slouží neformální protokol NSDraggingDestination – prostřednictvím jeho služeb je naopak view, jež je implementuje, informováno o tom, zda nad ním zrovna někdo něco táhne myší; služby umožňují na tažení průběžně reagovat, rozhodnout zda objekt přijmout nebo ne, atd.;
  • trojici doplňuje formální protokol NSDraggingInfo; jeho účelem je nést informace o taženém objektu v průběhu tažení. Mac OS X automaticky ve chvíli, kdy tažení zahájíme, vytvoří objekt který tomuto protokolu odpovídá, a jeho prostřednictvím pak právě view, jež implementuje odpovídající služby z protokolu NSDraggingDestination, má k dispozici všechny potřebné informace.

Chceme-li tedy sestavit view, jež bude sloužit jako zdroj tažení, implementujeme vhodné služby z protokolu NSDraggingSource; má-li naše view být s to přijímat objekty tažené myší, je třeba implementovat služby z protokolu NSDraggingDestination. Pro vlastní aktivaci tažení pak je zapotřebí ve vhodný okamžik – nejspíše tedy z metody mouseDragged:, již již dávno známe – poslat view standardní zprávu dragImage:at:offset:event:pasteboard:source:slideBack:. Vlastní "tažená data" pak se předávají prostřednictvím schránky: to již umíme, postup je zde týž, jako při ukládání dat do schránky v implementaci příkazu copy:.

Ukázka

Pojďme si to ukázat v praxi – vylepšíme naši krásnou grafickou aplikaci o možnost tahat myší grafické objekty.

Pomocné metody pro práci se schránkou

Začneme malým vylepšením služeb copy: a paste: – rozdělíme je a upravíme tak, abychom mohli hotového ukládání a načítání grafických objektů využívat nad libovolnou schránkou:

static NSString *
  const PBType=@"cz.ocs.TestViewApp.Pasteboard";
-(NSPasteboard*)copyToPasteboard:(NSPasteboard*)pboard {
  [pboard declareTypes:[NSArray arrayWithObject:PBType]
    owner:nil];
  NSArray *a=lines;
  if (selection<[lines count])
    a=[NSArray arrayWithObject:
      [lines objectAtIndex:selection]];
  [pboard setData:[NSArchiver archivedDataWithRootObject:a]
    forType:PBType];
  return pboard;
}
-(BOOL)pasteFromPasteboard:(NSPasteboard*)pboard {
  if (![pboard availableTypeFromArray:
    [NSArray arrayWithObject:PBType]]) return NO;
  NSArray *a=[NSUnarchiver unarchiveObjectWithData:
    [pboard dataForType:PBType]];
  [lines addObjectsFromArray:a];
  selection=1e6;
  [self setNeedsDisplay:YES];
  return YES;
}
-(void)copy:sender {
  if ([lines count]==0) NSBeep();
  else [self copyToPasteboard:
         [NSPasteboard generalPasteboard]];
}
-(void)paste:sender {
  if (![self pasteFromPasteboard:
         [NSPasteboard generalPasteboard]])
    NSBeep();
}

Srovnáme-li původní a současnou implementaci, vidíme, že se v zásadě nezměnilo nic – jen nyní máme navíc k dispozici pomocné služby copyToPasteboard: a pasteFromPasteboard:, jež dokáží uložit grafické objekty do, případně načíst z, libovolné schránky. Povšimněme si, že metoda copyToPasteboard: zdánlivě zbytečně vrací schránku, do níž právě data uložila – za chvilku uvidíme, jak se nám to bude hodit.

Vlastní tažení

Hned toho také využijeme: do metody mouseDragged: totiž doplníme kód, který v případě, že je při tažení držen přepínač alt, namísto dosavadní činnosti spustí tažení dat. K tomu stačí, jak jsme si již řekli, pošleme-li "sami sobě" standardní zprávu dragImage:at:offset:event:pasteboard:source:slideBack: – asi nějak takto:

-(void)mouseDragged:(NSEvent*)evt {
  if ([evt modifierFlags]&NSAlternateKeyMask)
    NSImage *img=[[[NSImage alloc]
      initWithSize:[self bounds].size] autorelease];
    [img lockFocus];
    [self drawRect:[self bounds]];
    [img unlockFocus];
    [self dragImage:img
      at:[self convertPoint:[evt locationInWindow] fromView:nil]
      offset:NSZeroSize
      event:evt
      pasteboard:[self copyToPasteboard:
        [NSPasteboard pasteboardWithName:NSDragPboard]]
      source:self
      slideBack:YES];
  else [self setNeedsDisplay:YES];
}

První řádek je zcela zřejmý – v něm zjistíme, je-li stisknut přepínač alt. Je-li tomu tak, pak na následujících několika řádcích vytvoříme obrázek, obsahující okamžitý "snímek" našeho view – ačkoli přebohaté služby a možnosti třídy NSImage zatím "nebereme", toto je hezká finta, jež se může často hodit: vytvoříme novou prázdnou instanci třídy NSImage v téže velikosti, jakou má naše view ([self bounds].size). Zpráva lockFocus zajistí, že jakékoli následující kreslení se provede právě do obrázku, jemuž jsme tuto zprávu poslali – takže následující [self drawRect:...] prostě a jednoduše vykreslí naše view, ale nikoli na obrazovku jako obvykle, nýbrž právě do nově vytvořeného obrázku. Zprávou unlockFocus jen tento speciální kreslicí režim opět ukončíme.

Argumenty zprávy dragImage:at:offset:event:pasteboard:source:slideBack: jsou následující:

  • dragImage: je obrázek, který representuje tažená data – zde právě použijeme čerstvě vytvořený "snímek";
  • na souřadnicích at: se obrázek objeví (a pak bude dále "vezen" spolu s kursorem myši) – obvykle se proto použije právě souřadnice kursoru myši, jen v případě, že bychom např. chtěli obrázek táhnout "za prostředek" (namísto levého dolního rohu), souřadnice bychom patřičným způsobem upravili;
  • offset: je přežitek starších versí systému a nijak se nepoužívá;
  • event: je instance třídy NSEvent, v jejímž zpracování jsme tažení spustili;
  • pasteboard: je schránka, v níž jsou uložena "tažená data". Zde právě využijeme pomocnou metodu copyToPasteboard: – díky tomu, že z ní vracíme nově naplněnou schránku, je to velmi pohodlné. Pro tažení se standardně využívá systémová schránka, již získáme na základě standardního jména NSDragPboard;
  • source: je libovolný objekt, který implementuje (alespoň některé) z metod neformálního protokolu NSDraggingSource, a který tažení řídí. Obvykle to bývá právě view, jež tažení spustilo (jako tomu je zde); ve složitějších aplikacích to však nezřídka bývá spíše nějaký kontrolér. K tomu se za chvilinku ještě vrátíme;
  • konečně argument slideBack: prostě jen říká, zda se má tažený obrázek automaticky animovaně vrátit zpět v případě, že cíl, nad nímž objekt "upustíme", jej nepřijme.

Kontrolér datového zdroje

Tím jsme s podporou tažení téměř hotovi; poslední, co musíme udělat, aby tažení skutečně fungovalo, je implementovat některé z metod neformálního protokolu NSDraggingSource. Jako nutné minimum stačí jedna jediná; framework ji zavolá při začátku tažení, aby zjistil, které operace jsou k dispozici (a umožnil uživateli mezi nimi volit pomocí standardních přepínačů). V Cocoa jsou k dispozici následující možné operace:

  • NSDragOperationCopy: kopírování objektů;
  • NSDragOperationLink: vytvoření odkazu na objekty;
  • NSDragOperationMove: přemístění objektů;
  • NSDragOperationDelete: zrušení objektů;
  • NSDragOperationGeneric: blíže neurčená obecná operace;
  • NSDragOperationEvery: lze zvolit libovolnou operaci;
  • NSDragOperationNone: v současné situaci není žádná operace možná.

a metoda draggingSourceOperationMaskForLocal: je může vrátit třeba všechny (spojené operací bitového součtu, tj. '|'). V naší jednoduché aplikaci nabídneme pouze kopírování:

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

Argument metody bychom mohli využít v případě, že bychom chtěli nabídnout odlišnou skupinu operací pro tažení do ostatních oken a views téže aplikace (local=YES) či aplikací ostatních (local=NO). V našem případě se tím opět nebudeme obtěžovat; jinak je ale zřejmé, že kupříkladu ve většině aplikací bude patrně operace NSDragOperationLink k dispozici pouze lokálně (a třeba pouze v rámci téhož dokumentu – jak takové věci zjistit, to si ukážeme příště, až se budeme zabývat naopak podporou pro vhazování).

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: