Rechts und Links-Klick unterscheiden: NSStatusBar

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Leider schon…

    90% der Methoden sind obsolete zumal -setView: zwar noch möglich ist, aber das enthaltene View irgendwie nicht mehr richtig "funktioniert".
    Die ganzen Mouse-Events unter Yosemite funktionieren nicht mehr korrekt bzw. werde nicht mehr richtig verarbeitet.
    Zumal ab jetzt der Darmkode berücksichtig werden muss.

    Mit NSStatusBarButton hingegen funktioniert alles tadellos.
    Wer eine neue Anwendung entwickelt, der ist mit -setView: schlecht beraten und sollte auf NSStatusBarButton setzen.

    Ist halt die Frage, wer die Anwendung nutzt.
    Bei meiner Status-Item-Anwndung sind über 70% schon auf Yosemite.

    Viele Grüße
  • Täglich grüßt das Murmeltier ;) Guten Morgen!

    Ich habe NSView jetzt mit einer Subklasse ausgestattet, in der ich mouseDown und rightMouseDown mittels override auswerten kann.

    Ich denke es fehlt jetzt nur noch eine Kleinigkeit: Durch das statusItem.button?.addSubview(statusItemSubview) füge ich am NSStatusItem zumindest keine spürbare Änderung herbei. Den View habe ich provisorisch wie folgt deklariert:

    Quellcode

    1. let statusItemSubview = MyView()
    2. statusItemSubview.drawRect(NSRect(x: 0, y: 0, width: 16, height: 16))


    Ich vermute dies Views werden in Ebenen gezeichnet? Muss ich den Subview in der Reihenfolge ganz nach vorne holen (wie?)?
  • Du solltest die Größe von dem View vorab ermitteln und Dir von der StatusBar geben lassen.
    Da gibt es ja -thickness…

    Des weiteren solltest Du Deinem View eine AutoResizingMask geben, z.B. NSIntegerMax.
    Dein View muss auch etwas Sichtbares, wie ne Hintergrundfarbe oder so enthalten, sonst siehst Du natürlich nichts.

    Dann sollte es auch schon funktionieren… ;)

    Viele Grüße
  • Über statusItem.view?.bounds erhalte ich leider nur nil. Wie ermittle ich die Größe des Views?

    Ich habe meine NSView-Subklasse um folgendes ergänzt - zu sehen ist nichts.

    Quellcode

    1. ​override func drawRect(dirtyRect: NSRect)
    2. {
    3. super.drawRect(dirtyRect)
    4. self.needsDisplay = true
    5. NSColor.redColor().setFill()
    6. NSRectFill(dirtyRect)
    7. }
  • Danke für den Hinweis - setNeedsDisplay ist wieder rausgeflogen :)

    In Bezug auf die View-Hierarchie bin ich leider nicht weiter gekommen. Ich kenne lediglich die Option im IB. Wie steuere ich das programmatisch an? addSubview allein genügt nicht? Der parent steht dadurch ja eigentlich fest.
  • Hallo,

    da ich gut gelaunt bin habe ich Dir das mal runtergetippt:

    Dur legst zwei Klassen an "TTStatusItemController" und "TTView".
    Zu Swift darfst Du es selbst übertragen. Dafür bin ich zu plöht…

    Viele Grüße

    AppDelegate.m

    Quellcode

    1. #import "AppDelegate.h"
    2. #import "TTStatusItemController.h"
    3. @interface AppDelegate ()
    4. @property (retain) TTStatusItemController *contentStatusItemController;
    5. @end
    6. @implementation AppDelegate
    7. - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
    8. {
    9. // …
    10. self.contentStatusItemController = [[TTStatusItemController alloc] init];
    11. // …
    12. }
    13. @end
    Alles anzeigen


    TTStatusItemController.m

    Quellcode

    1. #import "TTStatusItemController.h"
    2. #import "TTView.h"
    3. @interface TTStatusItemController ()
    4. @property (readwrite, retain) NSStatusItem *contentStatusItem;
    5. @end
    6. @implementation TTStatusItemController
    7. -(void)dealloc
    8. {
    9. // …
    10. [self hideStatusItem];
    11. // …
    12. self.contentStatusItem = nil;
    13. // …
    14. }
    15. -(instancetype)init
    16. {
    17. // …
    18. self = [super init];
    19. // …
    20. if(self == nil)
    21. {
    22. return nil;
    23. }
    24. // …
    25. [self showStatusItem];
    26. // …
    27. return self;
    28. // …
    29. }
    30. #pragma mark Actions
    31. -(BOOL)hideStatusItem
    32. {
    33. // …
    34. NSStatusItem *statusItem = self.contentStatusItem;
    35. if(statusItem == nil)
    36. {
    37. return NO;
    38. }
    39. // …
    40. NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
    41. if(statusBar == nil)
    42. {
    43. return NO;
    44. }
    45. // …
    46. [statusBar removeStatusItem:statusItem];
    47. // …
    48. self.contentStatusItem = nil;
    49. // …
    50. return YES;
    51. // …
    52. }
    53. -(BOOL)showStatusItem
    54. {
    55. // …
    56. NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
    57. if(statusBar == nil)
    58. {
    59. return NO;
    60. }
    61. // …
    62. NSStatusItem *statusItem = [statusBar statusItemWithLength:NSSquareStatusItemLength];
    63. if(statusItem == nil)
    64. {
    65. return NO;
    66. }
    67. // …
    68. NSStatusBarButton *statusBarButton = [statusItem button];
    69. if(statusBarButton == nil)
    70. {
    71. return NO;
    72. }
    73. // …
    74. [statusBarButton setTitle:nil];
    75. [[statusBarButton cell] setImageScaling:NSImageScaleProportionallyDown];
    76. [statusBarButton sendActionOn:NSRightMouseUpMask];
    77. [statusBarButton setTarget:nil];
    78. [statusBarButton setAction:NULL];
    79. // …
    80. NSRect rect = [statusBarButton frame];
    81. rect.origin = NSZeroPoint;
    82. // …
    83. TTView *view = [[TTView alloc] initWithFrame:rect];
    84. if(view == nil)
    85. {
    86. return NO;
    87. }
    88. // …
    89. [view setAutoresizingMask:NSUIntegerMax];
    90. // …
    91. [statusBarButton addSubview:view];
    92. // …
    93. self.contentStatusItem = statusItem;
    94. // …
    95. return YES;
    96. // …
    97. }
    98. @end
    Alles anzeigen


    TTView.m

    Quellcode

    1. #import "TTView.h"
    2. @implementation TTView
    3. -(void)mouseDown:(NSEvent*)theEvent
    4. {
    5. // …
    6. [super mouseDown:theEvent];
    7. // …
    8. NSLog(@"-mouseDown:");
    9. // …
    10. }
    11. -(void)rightMouseDown:(NSEvent *)theEvent
    12. {
    13. // …
    14. [super rightMouseDown:theEvent];
    15. // …
    16. NSLog(@"-rightMouseDown:");
    17. // …
    18. }
    19. #pragma mark Draw
    20. -(void)drawRect:(NSRect)dirtyRect
    21. {
    22. // …
    23. [super drawRect:dirtyRect];
    24. // …
    25. [self drawBackgroundColorInRect:dirtyRect];
    26. // …
    27. }
    28. -(BOOL)drawBackgroundColorInRect:(NSRect)dirtyRect
    29. {
    30. // …
    31. NSColor *color = [NSColor redColor];
    32. if(color == nil)
    33. {
    34. color = [NSColor clearColor];
    35. }
    36. // …
    37. if(color == nil)
    38. {
    39. return NO;
    40. }
    41. // …
    42. [color set];
    43. // …
    44. NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
    45. // …
    46. return YES;
    47. // …
    48. }
    49. @end
    Alles anzeigen
  • Lieber little_pixel, vielen vielen Dank!

    Ich war offen gestanden nicht in der Lage Zeile für Zeile zu übersetzen, da ich bisher keine Berührungspunkte mit Obj-C hatte. Sehr gut aber, dass du bereits Platz für die Auskommentierung vorgesehen hast :P Tatsächlich habe ich es jetzt mit Swift zum Laufen bringen können.

    Quellcode

    1. let frame = statusItem.button?.bounds // Größe ermitteln
    2. let testView = LxView()
    3. testView.frame = CGRectMake(0, 0, CGRectGetWidth(frame!)/2, CGRectGetHeight(frame!)) // Größe setzen
    4. statusItem.button?.addSubview(testView) // View hinzufügen


    Im drawRect der Subklasse mache ich nicht viel:

    Quellcode

    1. ​override func drawRect(dirtyRect: NSRect)
    2. {
    3. super.drawRect(dirtyRect)
    4. }


    Die Links/Rechts-Klick kann ich jetzt aber abfangen.
    Bleiben nur zwei kleine Probleme:

    1) Der Subview soll "unsichtbar" über dem NStatusBarButton liegen. Tatsächlich lässt er den darunter liegenden View aber leicht verblassen. Ein Fill mit NSColor.clearColor().setFill() bewirkt genau das Gegenteil von dem, was ich erhofft hatte ;) Wie kann ich den View tatsächlich "unsichtbar" zeichnen? drawRect ist ja wie geschrieben recht leer.

    2) Ich kann zwar den StatusBarButton mittels .highlighted = true bei Aktivierung highlighten, aber unter dem Subview ist dies nicht zu erkennen (sieht so aus, als würde gar nichts geschehen). Evtl. gleiche Lösung wie in 1)?

    Viele Grüße
    Martin
  • Hallo,

    ich hole mal kurz aus…

    "Damals" gab es ja die Methode -setView:, das das komplette Standardverhalten von dem normalen NSStatusItem "ausgeschaltet" hat.
    D.h. Darstellung uns Aktionen mussten komplett selbst in dem View "nachgebaut" werden.

    Diese Methode ist deprecated und als solches gibt es keinen konkreten Ersatz.
    Stattdessen kann jetzt mit -addSubview: dem Knopf von NSStatusBarButton das eigene View hinzugefügt werden.
    Das ist aber als solches nicht Sinn der Sache und geht schon in die Richtung Vergewaltung. Schließlich ist das schon ein Action-Element, das sämtliche Funktionalität mit sich bringt.

    Lange Rede kurzer Sinn:
    Du willst Dir gerade beide Vorteile verschaffen. Darstellung vom Knopf und Mausaktionen vom eigenen View.
    Das kollidiert leider. Ich denke nicht, dass das mit dem -addSubview: von Apple so angedacht ist.
    Da es aber alternativlos ist, ist das der einzige Weg das Ding zu individualisieren.

    Lagere alles in Dein eigenes View aus, eben auch das Icon, dann wird alles wunderbärchen funktionieren.

    Viele Grüße
  • Noch zur Ergänzung:

    In Deinem Fall würde ja eigentlich reichen…

    [statusBarButton sendActionOn:(NSLeftMouseUpMask|NSRightMouseUpMask)];


    Das funktioniert aber leider gar nicht mit der rechten Maustaste.
    Deshalb bleibt nur selbst machen.

    Würde es funktionieren, dann könntest Du in Deiner Methode einfach mit [NSEvent pressedMouseButtons] prüfen…

    Viele Grüße
  • Ich hatte noch gar nicht mit dem neusten Update von 10.10 getestet.
    Gute Nachrichten für Dich!

    Du brauchst das View nicht, den sie haben den Bug behoben.

    Viele Grüße

    Quellcode

    1. [statusBarButton sendActionOn:(NSLeftMouseUpMask | NSRightMouseUpMask)];
    2. -(BOOL)someAction:(id)sender
    3. {
    4. // …
    5. NSEvent *event = [NSApp currentEvent];
    6. if(event == nil)
    7. {
    8. return NO;
    9. }
    10. // …
    11. NSEventType eventType = [event type];
    12. if(eventType == NSLeftMouseUp)
    13. {
    14. // …
    15. NSLog(@"NSLeftMouseUp");
    16. // …
    17. }
    18. else if(eventType == NSRightMouseUp)
    19. {
    20. // …
    21. NSLog(@"NSRightMouseUp");
    22. // …
    23. }
    24. else
    25. {
    26. // …
    27. return NO;
    28. // …
    29. }
    30. // …
    31. NSLog(@"… do it!");
    32. // …
    33. return YES;
    34. // …
    35. }
    Alles anzeigen
  • Ein Lichtblick! Danke.
    Für die Übersetzung deiner ersten Zeile brauche ich etwas mehr als nur eine Zeile ;)

    Quellcode

    1. ​override func awakeFromNib() {
    2. let clickLeft = Int(NSEventMask.LeftMouseUpMask.rawValue)
    3. let clickRight = Int(NSEventMask.RightMouseUpMask.rawValue)
    4. statusItem.button!.sendActionOn(clickLeft | clickRight)
    5. }


    Habe jetzt eine neue Subklasse für den NSStatusBarButton geschrieben um die Events zu handeln (richtig?). Da dieser Button aber automatisch dank
    ​let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)
    erzeugt wird - wie kann ich meine individuelle Subklasse zuordnen?