Refactor cursor handling from a pitch based approach to a grid based one

This commit is contained in:
Matthias Neeracher 2011-08-28 12:42:39 +02:00
parent c50fc4e885
commit ed2a79a74e
5 changed files with 88 additions and 212 deletions

View File

@ -63,6 +63,12 @@ enum VLRecalc {
kFirstRecalc
};
enum VLCursorVisual {
kCursorExtend = 1<<15,
kCursorFlagsMask= 0x8000,
kCursorNoPitch = -128
};
@class VLEditable;
@interface VLSheetView : NSView {
@ -76,9 +82,8 @@ enum VLRecalc {
VLRegion fCursorRegion;
int fCursorMeasure;
VLFract fCursorAt;
int fCursorPitch;
int fCursorActualPitch;
VLMusicElement fCursorAccidental;
int fCursorVertPos;
uint16_t fCursorVisual;
size_t fCursorStanza;
int fSelStart;
int fSelEnd;
@ -121,11 +126,11 @@ enum VLRecalc {
- (float) systemY:(int)system;
- (int) gridInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual;
- (float) noteYInGrid:(int)vertPos;
- (float) noteYInSection:(int)section withPitch:(int)pitch visual:(uint16_t *)visual;
- (float) noteYInSection:(int)section withPitch:(int)pitch;
- (VLMusicElement)accidentalForVisual:(uint16_t)visual;
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch visual:(uint16_t *)visual;
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch;
- (float) noteYInMeasure:(int)measure withGrid:(int)vertPos;
- (float) noteXInMeasure:(int)measure at:(VLFraction)at;
- (void) scrollMeasureToVisible:(int)measure;

View File

@ -96,7 +96,8 @@ static float sFlatPos[] = {
fNeedsRecalc = kFirstRecalc;
fClickMode = ' ';
fDisplayScale = 1.0f;
fCursorPitch = VLNote::kNoPitch;
fCursorVertPos = 0;
fCursorVisual = 0;
fSelStart = 0;
fSelEnd = -1;
fNumTopLedgers = 0;
@ -152,12 +153,17 @@ static float sFlatPos[] = {
return VLPitchToGrid(pitch, visual, key);
}
- (float) noteYInGrid:(int)vertPos
{
return (vertPos*0.5f - 1.0) * kLineH;
}
- (float) noteYInSection:(int)section withPitch:(int)pitch visual:(uint16_t *)visual
{
int key = [self song]->fProperties[section].fKey;
int grid = VLPitchToGrid(pitch, *visual, key);
return (grid*0.5f - 1.0) * kLineH;
return [self noteYInGrid:grid];
}
- (float) noteYInSection:(int)section withPitch:(int)pitch
@ -166,7 +172,7 @@ static float sFlatPos[] = {
uint16_t visual = 0;
int grid = VLPitchToGrid(pitch, visual, key);
return (grid*0.5f - 1.0) * kLineH;
return [self noteYInGrid:grid];
}
- (VLMusicElement)accidentalForVisual:(uint16_t)visual
@ -187,20 +193,10 @@ static float sFlatPos[] = {
}
}
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch visual:(uint16_t *)visual
- (float) noteYInMeasure:(int)measure withGrid:(int)vertPos
{
return [self systemY:fLayout->SystemForMeasure(measure)]
+ [self noteYInSection:[self song]->fMeasures[measure].fPropIdx
withPitch:pitch visual:visual];
}
- (float) noteYInMeasure:(int)measure withPitch:(int)pitch
{
uint16_t dummyVis = 0;
return [self systemY:fLayout->SystemForMeasure(measure)]
+ [self noteYInSection:[self song]->fMeasures[measure].fPropIdx
withPitch:pitch visual:&dummyVis];
+ [self noteYInGrid:vertPos];
}
- (float) noteXInMeasure:(int)measure at:(VLFraction)at
@ -674,109 +670,38 @@ const char * sBreak[3] = {"", "\xE2\xA4\xBE", "\xE2\x8E\x98"};
[fFieldEditor setAction:nil];
}
const float kSemiFloor = -5.0f*kLineH;
static int8_t sSemiToPitch[] = {
47, // B
48, 50, // D
52, 53, // F
55, 57, // A
59, 60, // Middle C
62, 64, // E
65, 67, // G
69, 71, // B
72, 74, // D
76, 77, // F
79, 81, // A
83, 84, // C
86, 88, // E
89, 91, // G
93, 95, // B
96, 98 // D
};
static int8_t sFlatAcc[] = {
6, // Cb
11,
4, // Db
9,
2, // Eb
7, // Fb
12,
5, // Gb
10,
3, // Ab
8,
1, // Bb
};
static int8_t sSharpAcc[] = {
2, // C# is the 2nd sharp
9,
4, // D#
11,
6, // E#
1, // F#
8,
3, // G#
10,
5, // A#
12,
7, // B#
};
const float kSemiFloor = -1.0f*kLineH;
- (void) accidentalFromEvent:(NSEvent *)event
{
fCursorAccidental = (VLMusicElement)0;
//
// Extension
//
if (([event modifierFlags] & (NSShiftKeyMask|NSAlternateKeyMask|NSCommandKeyMask))==NSShiftKeyMask) {
fCursorAccidental = kMusicExtendCursor;
return;
}
int cursorSection =
[self song]->fMeasures[fCursorMeasure].fPropIdx;
const VLProperties & prop =
[self song]->fProperties[cursorSection];
switch ([event modifierFlags] & (NSShiftKeyMask|NSAlternateKeyMask|NSCommandKeyMask)) {
case NSShiftKeyMask:
fCursorVisual = kCursorExtend;
break;
case NSShiftKeyMask|NSAlternateKeyMask:
fCursorAccidental = kMusic2FlatCursor; // Gbb
fCursorActualPitch = fCursorPitch-2;
fCursorVisual = VLNote::kWant2Flat; // Gbb
break;
case NSAlternateKeyMask:
fCursorAccidental = kMusicFlatCursor; // Gb
fCursorActualPitch = fCursorPitch-1;
fCursorVisual = VLNote::kWantFlat; // Gb
break;
case NSShiftKeyMask|NSCommandKeyMask:
fCursorAccidental = kMusic2SharpCursor; // G##
fCursorActualPitch = fCursorPitch+2;
fCursorVisual = VLNote::kWant2Sharp; // G##
break;
case NSCommandKeyMask:
fCursorAccidental = kMusicSharpCursor; // G#
fCursorActualPitch = fCursorPitch+1;
fCursorVisual = VLNote::kWantSharp; // G#
break;
case NSAlternateKeyMask|NSCommandKeyMask:
fCursorAccidental = kMusicNaturalCursor; // G
fCursorActualPitch = fCursorPitch;
fCursorVisual = VLNote::kWantNatural; // G
break;
default:
switch (VLVisualInKey(fCursorPitch, prop.fKey)) {
case VLNote::kWantFlat:
fCursorActualPitch = fCursorPitch-1;
case VLNote::kWantSharp:
fCursorActualPitch = fCursorPitch+1;
default:
fCursorActualPitch = fCursorPitch;
}
fCursorVisual = 0;
break;
}
}
- (VLRegion) findRegionForEvent:(NSEvent *) event
{
fCursorPitch = VLNote::kNoPitch;
fCursorVertPos = kCursorNoPitch;
NSPoint loc = [event locationInWindow];
loc = [self convertPoint:loc fromView:nil];
@ -841,8 +766,7 @@ static int8_t sSharpAcc[] = {
}
loc.y -= kSystemBaseline+kSemiFloor;
int semi = static_cast<int>(roundf(loc.y / (0.5f*kLineH)));
fCursorPitch = sSemiToPitch[semi];
fCursorVertPos = static_cast<int>(roundf(loc.y / (0.5f*kLineH)));
[self accidentalFromEvent:event];
@ -854,16 +778,16 @@ static int8_t sSharpAcc[] = {
if ([event modifierFlags] & NSAlphaShiftKeyMask)
return; // Keyboard mode, ignore mouse
bool hadCursor = fCursorPitch != VLNote::kNoPitch;
bool hadCursor = fCursorRegion == kRegionNote;
[self findRegionForEvent:event];
bool hasCursor = fCursorPitch != VLNote::kNoPitch;
bool hasCursor = fCursorRegion == kRegionNote;
[self setNeedsDisplay:(hadCursor || hasCursor)];
}
- (void)flagsChanged:(NSEvent *)event
{
if (fCursorPitch != VLNote::kNoPitch) {
if (fCursorRegion == kRegionNote) {
[self accidentalFromEvent:event];
[self setNeedsDisplay:YES];
}
@ -877,7 +801,7 @@ static int8_t sSharpAcc[] = {
- (void) mouseExited:(NSEvent *)event
{
fCursorPitch = VLNote::kNoPitch;
fCursorRegion = kRegionNowhere;
[[self window] setAcceptsMouseMovedEvents:NO];
[self setNeedsDisplay:YES];
}
@ -919,14 +843,6 @@ static int8_t sSharpAcc[] = {
NSString * k = [event charactersIgnoringModifiers];
switch ([k characterAtIndex:0]) {
case '\r':
[self startKeyboardCursor];
[self addNoteAtCursor];
break;
case ' ':
[self startKeyboardCursor];
VLSoundOut::Instance()->PlayNote(VLNote(1, fCursorPitch));
break;
case 'r':
if (fClickMode == 'r')
fClickMode = ' ';

View File

@ -14,8 +14,7 @@
- (void) drawNotesForSystem:(int)system;
- (void) addNoteAtCursor;
- (void) startKeyboardCursor;
- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at accidental:(VLMusicElement)accidental;
- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at visual:(uint16_t)visual;
@end

View File

@ -21,58 +21,31 @@
- (void) addNoteAtCursor
{
if (fCursorMeasure > -1 && fCursorActualPitch) {
VLNote newNote(1, fClickMode==' ' ? fCursorActualPitch : VLNote::kNoPitch);
switch (fCursorAccidental) {
case kMusicFlatCursor:
newNote.fVisual |= VLNote::kWantFlat;
break;
case kMusicSharpCursor:
newNote.fVisual |= VLNote::kWantSharp;
break;
case kMusic2FlatCursor:
newNote.fVisual |= VLNote::kWant2Flat;
break;
case kMusic2SharpCursor:
newNote.fVisual |= VLNote::kWant2Sharp;
break;
case kMusicNaturalCursor:
newNote.fVisual |= VLNote::kWantNatural;
break;
default:
break;
}
if (fCursorMeasure > -1 && fCursorVertPos != kCursorNoPitch) {
[[self document] willChangeSong];
if (fCursorAccidental == kMusicExtendCursor)
newNote = [self song]->ExtendNote(fCursorMeasure, fCursorAt);
else if (fClickMode == 'k')
if (fCursorVisual == kCursorExtend) {
VLNote oldNote = [self song]->ExtendNote(fCursorMeasure, fCursorAt);
VLSoundOut::Instance()->PlayNote(oldNote);
} else if (fClickMode == 'k') {
[self song]->DelNote(fCursorMeasure, fCursorAt);
else
[self song]->AddNote(VLLyricsNote(newNote), fCursorMeasure, fCursorAt);
[[self document] didChangeSong];
} else {
int pitch = VLNote::kNoPitch;
if (fClickMode == ' ')
pitch = VLGridToPitch(fCursorVertPos, fCursorVisual,
[self song]->Properties(fCursorMeasure).fKey);
VLNote newNote(1, pitch, fCursorVisual & ~kCursorFlagsMask);
[self song]->AddNote(VLLyricsNote(newNote), fCursorMeasure, fCursorAt);
VLSoundOut::Instance()->PlayNote(newNote);
else
fClickMode = ' ';
}
[[self document] didChangeSong];
}
}
- (void) startKeyboardCursor
{
if (fCursorMeasure < 0) {
fCursorMeasure = 0;
fCursorPitch = VLNote::kMiddleC;
fCursorActualPitch = fCursorPitch;
fCursorAt = VLFraction(0);
}
}
- (void) drawLedgerLinesInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual at:(NSPoint)p
- (void) drawLedgerLines:(int)vertPos at:(NSPoint)p
{
p.x += kLedgerX;
int step = ([self gridInSection:section withPitch:pitch visual:visual]-2)/2;
int step = (vertPos-2) / 2;
for (int i=0; i-- > step; ) {
NSPoint p0 = p;
p0.y += i*kLineH;
@ -89,36 +62,37 @@
}
}
- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at
accidental:(VLMusicElement)accidental
mode:(char)mode
- (void) drawLedgerLinesInSection:(int)section withPitch:(int)pitch visual:(uint16_t)visual at:(NSPoint)p
{
[self drawLedgerLines:[self gridInSection:section withPitch:pitch visual:visual] at:p];
}
- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at
visual:(uint16_t)visual mode:(char)mode
{
int cursorX;
int cursorY;
int cursorSect;
VLMusicElement cursorElt;
VLMusicElement accidental = mode ? [self accidentalForVisual:visual] : kMusicNothing;
cursorX = [self noteXInMeasure:measure at:at];
if (accidental == kMusicExtendCursor) {
cursorY = [self noteYInMeasure:measure withPitch:pitch];
cursorElt = accidental;
if (visual == kCursorExtend) {
cursorY = [self noteYInGrid:vertPos];
cursorElt = kMusicExtendCursor;
} else {
uint16_t visual = 0;
switch (mode) {
default:
cursorY = [self noteYInMeasure:measure withPitch:pitch visual:&visual] - kNoteY;
cursorSect = [self song]->fMeasures[measure].fPropIdx;
[self drawLedgerLinesInSection:cursorSect withPitch:pitch
visual:visual at:NSMakePoint(cursorX,
cursorY = [self noteYInMeasure:measure withGrid:vertPos] - kNoteY;
[self drawLedgerLines:vertPos at:NSMakePoint(cursorX,
[self systemY:fLayout->SystemForMeasure(measure)])];
cursorElt = kMusicNoteCursor;
break;
case 'r':
cursorY = [self noteYInMeasure:measure withPitch:65];
cursorY = [self noteYInMeasure:measure withGrid:3];
cursorElt = kMusicRestCursor;
break;
case 'k':
cursorY = [self noteYInMeasure:measure withPitch:pitch];
cursorY = [self noteYInGrid:vertPos];
cursorElt = kMusicKillCursor;
break;
}
@ -128,9 +102,11 @@
[[self musicElement:cursorElt]
compositeToPoint:xy
operation: NSCompositeSourceOver];
if (mode && accidental && accidental != kMusicExtendCursor) {
if (accidental) {
xy.y += kNoteY;
switch (cursorElt= accidental) {
(int &)accidental += kMusicFlatCursor-kMusicFlat;
switch (accidental) {
case kMusicFlatCursor:
xy.x += kFlatW;
xy.y += kFlatY;
@ -152,22 +128,21 @@
xy.y += kNaturalY;
break;
}
[[self musicElement:cursorElt]
[[self musicElement:accidental]
compositeToPoint:xy
operation: NSCompositeSourceOver];
}
}
- (void) drawNoteCursor:(int)pitch inMeasure:(size_t)measure at:(VLFract)at
accidental:(VLMusicElement)accidental
- (void) drawNoteCursor:(int)vertPos inMeasure:(size_t)measure at:(VLFract)at visual:(uint16_t)visual
{
[self drawNoteCursor:pitch inMeasure:measure at:at accidental:accidental mode:0];
[self drawNoteCursor:vertPos inMeasure:measure at:at visual:visual mode:0];
}
- (void) drawNoteCursor
{
[self drawNoteCursor:fCursorPitch inMeasure:fCursorMeasure at:fCursorAt
accidental:fCursorAccidental mode:fClickMode];
[self drawNoteCursor:fCursorVertPos inMeasure:fCursorMeasure at:fCursorAt
visual:fCursorVisual mode:fClickMode];
}
- (void) drawNote:(int)visual at:(NSPoint)p
@ -390,7 +365,6 @@
accidental:[self accidentalForVisual:filterVisuals(step, visual)]
tied:tied];
} else {
VLMusicElement accidental;
pos = NSMakePoint([self noteXInMeasure:measIdx at:at],
kSystemY+[self noteYInSection:measure.fPropIdx withPitch:65]);
[self drawRest:note->fVisual & VLNote::kNoteHeadMask at: pos];
@ -415,7 +389,7 @@
if (hasTriplets) {
[self drawTripletBracketFrom:tripletStartX to:tripletEndX atY:tripletY];
}
if (fCursorPitch != VLNote::kNoPitch && fLayout->SystemForMeasure(fCursorMeasure) == system)
if (fCursorRegion == kRegionNote && fLayout->SystemForMeasure(fCursorMeasure) == system)
[self drawNoteCursor];
}

View File

@ -16,14 +16,15 @@
#import "VLSheetViewLyrics.h"
#import "VLSheetWindow.h"
#import "VLDocument.h"
#import "VLPitchGrid.h"
@interface VLPlaybackEditable : VLEditable {
VLSheetView * fView;
size_t fStanza;
size_t fNoteMeasure;
VLFract fNoteAt;
int fNotePitch;
VLMusicElement fNoteAccidental;
int fNoteVert;
uint16_t fNoteVisual;
size_t fChordMeasure;
VLFract fChordAt;
}
@ -50,29 +51,10 @@
{
VLMIDIUserEvent * event = (VLMIDIUserEvent *)[ev pointerValue];
if (event->fPitch) {
fNotePitch = event->fPitch;
fNoteAccidental = kMusicNothing;
switch (event->fVisual & VLNote::kAccidentalsMask) {
case VLNote::kWantFlat:
fNoteAccidental = kMusicFlatCursor;
break;
case VLNote::kWantSharp:
fNoteAccidental = kMusicSharpCursor;
break;
case VLNote::kWant2Flat:
fNoteAccidental = kMusic2FlatCursor;
break;
case VLNote::kWant2Sharp:
fNoteAccidental = kMusic2SharpCursor;
break;
case VLNote::kWantNatural:
fNoteAccidental = kMusicNaturalCursor;
break;
default:
break;
}
fNoteMeasure = event->fMeasure;
fNoteAt = event->fAt;
fNoteVisual = event->fVisual & VLNote::kAccidentalsMask;
fNoteVert = VLPitchToGrid(event->fPitch, fNoteVisual, [fView song]->Properties(fNoteMeasure).fKey);
fStanza = event->fStanza;
[fView highlightTextInStanza:fStanza measure:fNoteMeasure at:fNoteAt one:YES];
} else {
@ -85,8 +67,8 @@
- (void) highlightCursor
{
if (fNoteMeasure != 0x80000000 && fNotePitch != VLNote::kNoPitch)
[fView drawNoteCursor:fNotePitch inMeasure:fNoteMeasure at:fNoteAt accidental:fNoteAccidental];
if (fNoteMeasure != 0x80000000 && fNoteVert != kCursorNoPitch)
[fView drawNoteCursor:fNoteVert inMeasure:fNoteMeasure at:fNoteAt visual:fNoteVisual];
if (fChordMeasure != 0x80000000)
[fView highlightChordInMeasure:fChordMeasure at:fChordAt];
}