updateTrackingAreas()

Freitag

Membre confirmé
8 Janvier 2010
46
1
63
Bonjour !

J'affiche une alerte blocante en plein écran avec un bouton de fermeture en haut à gauche qui réagit au survol du curseur.
Cette fenêtre est donc redimensionnée à sa création. Il faut donc que j'update la TrackingArea mais je n'y arrive pas.
J'ai vu dans la documentation qu'il fallait appeler Super mais je sèche. :(
La fonction est bien appelée

Voici la class du bouton :
Bloc de code:
//
//  Close.swift
//  Minuteur
//
//  Created by Philippe Galmel on 06/05/2015.
//  Copyright (c) 2015 Philippe Galmel. All rights reserved.
//

import AppKit

class Close: NSButton {
   
    var trackingArea: NSTrackingArea!
    let options = NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveAlways
   
   
    override func awakeFromNib() {
//        let rect = superview!.convertRect(self.frame, toView: superview)
//        trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
//        superview!.addTrackingArea(trackingArea)
    }
   
    override func mouseEntered(theEvent: NSEvent) {
        self.image = NSImage(named: "Close")
        println("entered")
    }
   
    override func mouseExited(theEvent: NSEvent) {
        self.image = NSImage(named: "Close_alt")
        println("exited")
    }
   
    override func updateTrackingAreas() {
//        superview!.removeTrackingArea(trackingArea)
//        let rect = superview!.convertRect(self.frame, toView: superview)
//        trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
//        superview!.addTrackingArea(trackingArea)
//        superview!.updateTrackingAreas()
//        println("TrackingAreas updated \(rect)")
    }
   
}
Et le controller de la fenêtre :
Bloc de code:
//
//  VisualAlarmController.swift
//  Minuteur
//
//  Created by Philippe Galmel on 04/05/2015.
//  Copyright (c) 2015 Philippe Galmel. All rights reserved.
//

import Cocoa

class VisualAlarmController: NSWindowController {
   
    var message = ""
   
    //@IBOutlet weak var theView: NSView!
    @IBOutlet weak var messageLabel: NSTextField!
    @IBOutlet weak var closeButton: NSButton!
   
    @IBOutlet weak var infosLabel: NSTextField!
   
    @IBAction func closeButtonPressed(sender: AnyObject) {
        self.window!.close()
    }
   
   
    override func windowDidLoad() {

        var acceptsMouseMovedEvents: Bool {
            return true
        }
       
        println("\(acceptsMouseMovedEvents)")

        let screen = NSScreen.mainScreen()
        let screenWidth: CGFloat = screen!.frame.size.width
        let screenHeight = screen!.frame.size.height
        let viewWidth = screenWidth
        let viewHeight = self.window?.frame.height
        let rect: NSRect!
       
        self.messageLabel.font = NSFont(name: "Helvetica Neue Thin", size: self.messageLabel.font!.pointSize)
        self.messageLabel.stringValue = message
       
        self.window!.backgroundColor = backgroundColor.colorWithAlphaComponent(0.85)
        self.window!.level = Int(CGWindowLevelForKey(Int32(kCGScreenSaverWindowLevelKey)))
       
        if leftTime == 0 {
            rect = CGRectMake(0.0, 0.0, screenWidth, screenHeight)
            self.window!.ignoresMouseEvents = false
            self.window!.opaque = false
            let timerClose = NSTimer.scheduledTimerWithTimeInterval(60, target: self, selector: Selector("closeVisualAlarmWindow:"), userInfo: nil, repeats: false)
        }
        else {
            rect = CGRectMake(0.0, screenHeight / 2 - viewHeight! / 2.0, screenWidth, viewHeight!)
            self.window!.ignoresMouseEvents = true
            self.window!.opaque = false
            let timerClose = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("closeVisualAlarmWindow:"), userInfo: nil, repeats: false)
            closeButton.hidden = true
        }
       
        infosLabel.stringValue = "Rect: \(rect)"
       
        self.window!.setFrame(rect, display: true, animate: false)
        self.window!.makeKeyAndOrderFront(self.window!)
       
        println("\(closeButton.frame)")
       
        super.windowDidLoad()
       
    }
   
    func closeVisualAlarmWindow(aTimer: NSTimer) {
        self.window!.close()
    }
   
}

Un petit coup de main ? Un immense merci !
 
Bonjour c'est les bounds qui sont important ; la frame a bougé mais n'est pas encore updatée ; le rect est dirty ; mettez donc des fonds de couleur a vos views avec un layer ; je fais toujours cela ; en debug toutes mes vues ont un calque coloré.
 
Bonjour, un autre conseil de code, si je peux me permettre, même si il y a different "style"

Bloc de code:
class name <: other>
{
    func name()
    {
           if <cond> {
           } else  if <cond> {
           } else {
           }
         
           do
           {
           } while <cond>;
         
           while <cond>
           {
         
           }
    }
}

cela marche dans tous les languages ; cela a l'avantage de rendre le code compact, consistant donc lisible.
 
Bonjour, un autre conseil de code, si je peux me permettre, même si il y a different "style"

Bloc de code:
class name <: other>
{
    func name()
    {
           if <cond> {
           } else  if <cond> {
           } else {
           }
        
           do
           {
           } while <cond>;
        
           while <cond>
           {
        
           }
    }
}

cela marche dans tous les languages ; cela a l'avantage de rendre le code compact, consistant donc lisible.

Oui, c'est juste. :)
 
Bonjour c'est les bounds qui sont important ; la frame a bougé mais n'est pas encore updatée ; le rect est dirty ; mettez donc des fonds de couleur a vos views avec un layer ; je fais toujours cela ; en debug toutes mes vues ont un calque coloré.
Merci, je vais suivre ce conseil.
Maintenant, je suis toujours en carafe.
Bloc de code:
//
//  Close.swift
//  Minuteur
//
//  Created by Philippe Galmel on 06/05/2015.
//  Copyright (c) 2015 Philippe Galmel. All rights reserved.
//

import AppKit

class Close: NSButton {
 
    var trackingArea: NSTrackingArea!
 
    override func awakeFromNib() {
        let rect: NSRect = superview!.convertRect(self.frame, toView: superview)
        let options = NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveAlways
        trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
        superview!.addTrackingArea(trackingArea)
     
        println("trackingArea: \(rect.origin), \(rect.size)")
    }
 
    override func mouseEntered(theEvent: NSEvent) {
        self.image = NSImage(named: "Close")
        println("entered")
    }
 
    override func mouseExited(theEvent: NSEvent) {
        self.image = NSImage(named: "Close_alt")
        println("exited")
    }
 
    override func updateTrackingAreas() {
        superview!.removeTrackingArea(trackingArea)
        let rect = superview!.convertRect(self.frame, toView: superview)
        let options = NSTrackingAreaOptions.MouseEnteredAndExited | NSTrackingAreaOptions.MouseMoved | NSTrackingAreaOptions.ActiveAlways
        trackingArea = NSTrackingArea(rect: rect, options: options, owner: self, userInfo: nil)
     
        superview!.addTrackingArea(trackingArea)

        println("updateTrackingArea: \(rect.origin), \(rect.size)")
    } 
}
La fonction updateTrackingAreas() est bien appelée après le redimensionnement et rect revoie bien le placement de la nouvelle trackingArea mais elle n'est pas active.
Rien ne se passe au survol du bouton, mouseEntered et mouseExited ne sont jamais appelées.
J'ai testé sans le redimensionnement initial de la fenêtre et en fait rien ne se passe jamais au survol du bouton comme si la fenêtre ne répondait pas aux évènements souris.

Il doit manquer quelque chose mais quoi ?
La fenêtre est sans bar de titre mais c'est pareil en la laissant.

Capture.png


Après différents tests, je viens de m'apercevoir que si je reviens sur la fenêtre principale de l'application alors la fenêtre d'alerte réponds aux évènements souris.
Si je redimensionne la fenêtre d'alerte, elle ne répond plus.
Je précise que la fenêtre principale est déjà "toujours au-dessus" et que la fenêtre d'alerte vient elle encore au-dessus, niveau SreenSaver, pour masquer tout l'écran principal, ceci pour contraindre l'utilisateur à réagir.

Je débute et je suis encore loin d'être au point avec la gestion des vues.

Edit : Si je désactive la barre de titre, la fenêtre d'alerte ne répond plus aux messages Entered/Exited. Il semble que la barre de titre soit nécessaire non ?
 
Dernière édition:
Bonjour,
ok oubliez la barre ; cela doit être parce qu'un des éléments interne appel acceptsFirstResponder, vous devez devenir le firstResponder ; le symptôme de lose-focus / get-focus le montre:

votre nouveau contexte doit dire/informer qu'il est le firstResponder [; ou la vue qui accepte les événements ;] si je comprends bien ce qui se passe, c'est ce qu'il y a derrière votre modal view qui attend toujours les événements et vous placez un élément au-dessus qui n'écoute rien.

Ici la référence avec les explications ; comment la chaine des événements est renvoyée et gérée sur chaque élément ; le système (window server) traque la "souris" partout et ne "dispach" que sur les éléments qui en font la demande "acceptsFirstResponder" et qui ont le "focus"

https://developer.apple.com/library...f/occ/instp/NSResponder/acceptsFirstResponder

Bloc de code:
// ajouter ceci

override public var acceptsFirstResponder: Bool
{
      get { return true }
}

// puis essayer aussi ceci, mais comme c'est déjà un button cela devrait être vrai

/*
Override this method in a subclass to allow instances to respond to click-through.
This allows the user to click on a view in an inactive window, activating the view
with one click, instead of clicking first to make the window active and then clicking
the view. Most view objects refuse a click-through attempt, so the event simply
activates the window. Many control objects, however, such as instances of
NSButton and NSSlider, do accept them, so the user can immediately manipulate
the control without having to release the mouse button.
*/

override public func acceptsFirstMouse(theEvent: NSEvent!) -> Bool
{
     return true
}
 
Dernière édition:
Bonjour,

Merci et désolé pour le retard à la détente...
Rien n'y fait. Je n'y arrive pas. J'ai tout essayé dans tous les sens.
J'ai bien compris cependant que c'est un problème de focus.
 
Bonjour, avez-vous un projet-test que vous pouvez donner en lien et qui reproduit le problème?, car j'avoue humblement ne pas comprendre d'ou vient ce problème? c'est parfois difficile de se rendre compte sans avoir sous la main quelque chose de concret.
 
Je n'osais pas le faire. :) C'est possible un lien en privé du projet réel ? Il est encore petit. C'est mon premier projet Xcode.