Input Management - 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ů



Software

Input Management

20. listopadu 2006, 00.00 | V minulém dílu našeho seriálu, věnovaného programování ve vývojovém prostředí Cocoa, jsme si ukázali, kterak užívat základních služeb třídy NSView (přesněji řečeno, její nadtřídy NSResponder; vzhledem k tomu však, že nyní se učíme sestavovat vlastní views, není zapotřebí to rozlišovat) pro zpracování událostí, přišedších od klávesnice.

V minulém dílu našeho seriálu, věnovaného programování ve vývojovém prostředí Cocoa, jsme si ukázali, kterak užívat základních služeb třídy NSView (přesněji řečeno, její nadtřídy NSResponder; vzhledem k tomu však, že nyní se učíme sestavovat vlastní views, není zapotřebí to rozlišovat) pro zpracování událostí, přišedších od klávesnice.

Naučili jsme se zkonfigurovat view tak, aby se mohlo stát tzv. first responderem a dostávalo tedy standardní zprávy keyDown:; zároveň jsme si také ale řekli, že ve většině případů je přímé využití zprávy keyDown: nepohodlné, a je daleko lepší využít vyšší úroveň služeb Cocoa pro zpracování vstupních dat, tzv. "input management". Jeho základům se právě budeme věnovat dnes.

Interpretace kláves

V zásadě se jedná o to, že si ponecháme stranou "ryze příkazové" kombinace kláves s přepínačem Command – ty se ostatně zpracovávají jinou cestou, již si popíšeme později, a v rámci zprávy keyDown: je vůbec nedostaneme – z klávesnice můžeme dostávat vstup, který se dělí do řady různých kategorií:

  • běžné znaky – písmena apod. – se prostě vloží do textu;
  • klávesy typu "delete" či "doplnění textu" mají trochu jiné postavení: sice text modifikují, avšak jedná se spíše o příkazy nežli o "vkládání textu" v pravém smyslu slova;
  • šipky nebo klávesy "page up"/"page down" dokonce ani nemění obsah dokumentu.

Nadto je v mnoha případech žádoucí, aby uživatel mohl konkrétní funkci jednotlivých kláves volně definovat; v Mac OS X tomu tak skutečně je, a slouží k tomu soubory "KeyBinding.dict".

O všechny tyto věci (a řadu dalších) se stará knihovní vrstva zvaná "input management". Nebudeme si nyní podrobně popisovat, jaké služby obsahuje a jak přesně fungují; naučíme se ji pouze využívat v našich aplikacích.

Služba interpretKeyEvents:

Z tohoto pohledu prostého použití služeb input managementu máme vlastně jediný "vstupní bod" do jeho systému: tím je standardní metoda interpretKeyEvents:, jejímž argumentem je pole událostí (instancí třídy NSEvent). Služba všechny zadané události zpracuje, vezme korektně v úvahu nastavení přepínačů a další atributy, převede stisknuté klávesy na vložení textu či příkazy, o nichž jsme hovořili v minulém odstavci – a o nich informuje view prostřednictvím řady speciálních "akčních" zpráv.

Tak kupříkladu pro vložení textu dostane view zprávu insertText:; jejímž argumentem je NSString obsahující patřičný textový řetězec. Pro posun o jeden znak doleva – typicky tedy po stisknutí šipky vlevo, pokud zároveň nebyly drženy žádné speciální přepínače, a pokud není tato akce uživatelsky předefinovaná (o to vše se starají knihovní služby input managementu) – dostane view zprávu modeLeft:; podobně je tomu s ostatními příkazy. Možných zpráv, jež input management může views posílat, je řada a všechny standardní (systém je dostatečně flexibilní na to, abychom prostřednictvím "key bindings" mohli definovat i zprávy vlastní) můžeme nalézt ve standardní dokumentaci třídy NSResponder. Zde si vyjmenujeme pouze několik nejběžnějších a nejčastěji využívaných:

  • cancelOperation: – zrušení právě probíhající operace, standardně odpovídá klávese "esc" či kombinaci "Command-.";
  • deleteBackward: – smazání znaku před kursorem, standardně odpovídá klávese "Backspace";
  • deleteForward: – smazání znaku za kursorem, standardně odpovídá klávese "Delete";
  • insertTab: – standardně odpovídá stisknutí klávesy "Tab";
  • insertNewline: – standardně odpovídá stisknutí klávesy "Enter";
  • insertText: – o této zprávě jsme se již zmiňovali, jejím prostřednictvím systém input managementu předává view "obyčejné" znaky pro vložení do textu. Jsou uvedeny v objektu třídy NSString, jenž je argumentem zprávy;
  • moveLeft:, moveUp:, moveDown: a moveRight: – přemísťování kursoru v textu, standardně odpovídá kursorovým šipkám;
  • pageUp: a pageDown: – přechod na minulou či příští stránku, standardně odpovídá stejnojmenným klávesám.

Jednoduchý výběr grafických prvků

Pojďme na chvilku zanechat teoretického povídání a trochu si zaprogramujeme: doplníme do naší grafické aplikace jednoduchou možnost výběru grafických prvků.

Nejprve samozřejmě implementujeme metodu keyDown: tak, abychom z ní spustili služby input managementu:

-(void)keyDown:(NSEvent*)event {
  [self interpretKeyEvents:
    [NSArray arrayWithObject:event]];
}

Pro vlastní výběr prvků samozřejmě použijeme standardní služby moveLeft: a moveRight:; nejprve si však musíme připravit proměnnou, v níž budeme udržovat index aktuálně zvoleného prvku:

@interface View:NSView {
  // instances of NSBezierPath:
  NSMutableArray *lines;
  // selected, >[lines count] if none:
  unsigned selection;
}

Pro vlastní označování využijeme – abychom si ušetřili nějaké ify – přetékání a podtékání hodnot v proměnné typu unsigned: odečteme-li v takové proměnné jedničku od nuly, můžeme se spolehnout, že dostaneme "hodně vysoké číslo". Následující implementace tedy bude postupně procházet grafické prvky v obou směrech, přičemž po posledním z nich jednou neoznačí nic, a pak začne opět dokola:

-(void)moveLeft:sender {
  if (selection-->[lines count])
    selection=[lines count]-1;
  [self setNeedsDisplay:YES];
}
-(void)moveRight:sender {
  if (++selection>[lines count])
    selection=0;
  [self setNeedsDisplay:YES];
}

Samozřejmě, nesmíme zapomenout na inicializaci proměnné na "nic není označeno" po každém vložení nového grafického prvku:

-(void)mouseDown:(NSEvent*)evt {
  if ([evt modifierFlags]&NSShiftKeyMask)
    [self setNeedsDisplay:YES];
  else {
    NSBezierPath *bp=[NSBezierPath bezierPath];
    [bp moveToPoint:
      [self convertPoint:
        [evt locationInWindow] fromView:nil]];
    [lines addObject:bp];
    selection=1e6; // surely >[lines count]
  }
}

a nakonec se postaráme o kreslení. Možností bychom měli řadu, prozatím však využijeme té nejjednodušší: prostě označený grafický prvek vykreslíme jinou barvou:

-(void)drawRect:(NSRect)rect {
  [[NSColor whiteColor] set];
  NSRectFill(rect);
  unsigned n=0;
  for (id o,en=[lines objectEnumerator];o=[en nextObject];) {
    [n++==selection?
      [NSColor greenColor]:[NSColor blueColor] set];
    [o stroke];
  }
  ... // dále je vše při starém
}

"Alerty" v Cocoa

Nakonec ještě implementujeme mazání označeného prvku – samozřejmě, nic jednoduššího, stačí použít standardní metodu deleteBackward:. Zároveň si ale ukážeme jednu věc, jež sice s views přímo nesouvisí, avšak je velmi důležitá – totiž správný způsob, kterak v Cocoa zobrazovat "alerty", informační či dotazové panely – zde jeden takový budeme potřebovat pro otázku, zda uživatel skutečně chce smazat všechny grafické objekty, pokud nebyl žádný označen.

V Cocoa je považováno za nesprávné v takovémto případě zablokovat celou aplikaci modálním panelem; namísto toho je korektním řešením zobrazit "sheet", vázaný pouze na jediné konkrétní okno. To ovšem samozřejmě znamená, že musíme zobrazení "alertu" a zpracování jeho výsledku oddělit do dvou samostatných metod (aby mezi jejich provedením mohla aplikace bez obtíží dělat jiné věci) – asi takto:

-(void)alertClosed:(NSWindow*)sheet result:(int)result {
  if (result==NSAlertDefaultReturn) {
    [lines removeAllObjects];
    [self setNeedsDisplay:YES];
  }
}
-(void)deleteBackward:sender {
  if ([lines count]==0) return;
  if (selection<[lines count]) {
    [lines removeObjectAtIndex:selection];
    [self setNeedsDisplay:YES];
  } else
    NSBeginAlertSheet(
      @"Delete all graphics?", // title
      @"Yes, delete",@"No",nil, // buttons
      [self window], // window to which it belongs
      self, // this object will get ...
      @selector(alertClosed:result:), // ...this message
      NULL,NULL, // extra possibilities, unused
      @"Do you really want to delete %d graphics objects?",
      [lines count]); // alert body, as in NSLog
}

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  

Poslat článek

Nyní máte možnost poslat odkaz článku svým přátelům:

Váš e-mail:

(Není povinný)

E-mail adresáta:

Odkaz článku:

Vzkaz:

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: