NSUndoManager отменяет все изменения, сделанные во время mouseDrag с одной отменой - PullRequest
0 голосов
/ 02 февраля 2019

У меня есть объект NSView, который принимает события mouseDragged, которые изменяют положение объекта, нарисованного в его соответствующем представлении.В моем классе NSViewController он затем получает эту новую позицию и присваивает это новое значение моему объекту данных модели.Другими словами, каждый раз, когда вызывается событие mouseDragged, ViewController обновляет новую позицию.Таким образом, в той же области ViewController регистрирует событие отмены через NSUndoManager, чтобы позволить пользователю выполнить действие отмены.

Проблема возникает, когда в диспетчере отмены зарегистрировано несколько действий обновления позиции.Когда пользователь выполняет действие «Отменить», вместо того, чтобы из стека отмены извлекалось только самое последнее действие, остальные действия также отменяются.

Для справки, вот мой код:

// MyViewController.m
- (void)translateObject:(double)dx
          withYPosition:(double)dy
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double t_y = [[transformData objectForKey:@"translate_y"] doubleValue];

    t_x -= x;
    t_y -= y;

    [self translateShape:@[[NSNumber numberWithDouble:t_x],[NSNumberNumberWithDouble:t_y]]];
}

- (void)translateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(translateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Drag Move"];  
}

/* * * I have also done this:

- (void)translateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(unTranslateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Drag Move"];  
}

- (void)unTranslateShape:(NSArray*)val
{
    NSMutableDictionary* transformData = [self.transformData transform];

    double old_t_x = [[transformData objectForKey:@"translate_x"] doubleValue];
    double old_t_y = [[transformData objectForKey:@"translate_y"] doubleValue];
    double t_x = [[val objectAtIndex:0] doubleValue];
    double t_y = [[val objectAtIndex:1] doubleValue];

    [transformData setObject:[NSNumber numberWithDouble:t_x] forKey:@"translate_x"];
    [transformData setObject:[NSNumber numberWithDouble:t_y] forKey:@"translate_y"];
    [self.transformData setTransform:transformData];
    [self.glView setNeedsDisplay:YES];

    [[self undoManager] registerUndoWithTarget:self
                                  selector:@selector(translateShape:)
                                    object:@[[NSNumber numberWithDouble:old_t_x],
                                             [NSNumber numberWithDouble:old_t_y]]];

    [[self undoManager] setActionName:@"Shape Redo Drag Move"]; 
}

* * */

Ниже представлена ​​плохо составленная диаграмма того, что я ожидал, что отмена произойдет, в сравнении с тем, что я на самом деле наблюдаю:

enter image description here

Может кто-нибудь пролить свет на это?Спасибо.

1 Ответ

0 голосов
/ 05 февраля 2019

Благодаря подсказке Джеймса Буканека о настройке runLoopModes в NSUndoManager я смог выяснить, почему NSUndoManager отменял все мои зарегистрированные отмены во время событий mouseDown, mouseDragged и mouseUp.Оказывается, в моем случае NSUndoManager группирует все отмены, зарегистрированные событием (что, я полагаю, также означает любые действия отмены, зарегистрированные в событии mouseDragged) .Для простой регистрации отмен в вашем подклассе NSView должно работать следующее:

/** Simple case: Everything handled by NSView subclass **/

- (void)mouseDown:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to NO here
}

- (void)mouseDragged:(NSEvent *)event
{
    // Tell your undo manager to begin grouping
    // Register undo and redo actions here
    // Tell your undo manager to end grouping
}

- (void)mouseUp:(NSEvent *)event
{
    // Set your undo manager's groupsByEvent property to YES here
}

Поскольку я использую протокол для установки атрибутов моей модели в моем контроллере представления, я просто реализовал другой метод в этом упомянутом протоколе, которыйвызывается во время событий mouseDown / mouseUp моего NSView, чтобы впоследствии установить для GroupGyEvent значение NO и YES.Если кто-нибудь знает гораздо лучшую реализацию / решение этой проблемы, не стесняйтесь исправлять меня.Надеюсь, это поможет!

...