aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--cocoa/ScintillaCocoa.h13
-rw-r--r--cocoa/ScintillaCocoa.mm298
-rw-r--r--cocoa/ScintillaView.h29
-rw-r--r--cocoa/ScintillaView.mm352
-rw-r--r--src/Editor.cxx175
-rw-r--r--src/Editor.h7
-rw-r--r--src/ViewStyle.cxx9
-rw-r--r--src/ViewStyle.h4
8 files changed, 477 insertions, 410 deletions
diff --git a/cocoa/ScintillaCocoa.h b/cocoa/ScintillaCocoa.h
index 302e39d97..0c0448118 100644
--- a/cocoa/ScintillaCocoa.h
+++ b/cocoa/ScintillaCocoa.h
@@ -55,6 +55,7 @@
extern "C" NSString* ScintillaRecPboardType;
@class InnerView;
+@class MarginView;
@class ScintillaView;
@class FindHighlightLayer;
@@ -109,6 +110,8 @@ private:
bool capturedMouse;
+ bool enteredSetScrollingSize;
+
// Private so ScintillaCocoa objects can not be copied
ScintillaCocoa(const ScintillaCocoa &) : ScintillaBase() {}
ScintillaCocoa &operator=(const ScintillaCocoa &) { return * this; }
@@ -125,6 +128,7 @@ private:
FindHighlightLayer *layerFindIndicator;
protected:
+ Point GetVisibleOriginInMain();
PRectangle GetClientRectangle();
Point ConvertPoint(NSPoint point);
@@ -135,17 +139,19 @@ protected:
virtual void CancelModes();
public:
- ScintillaCocoa(InnerView* view);
+ ScintillaCocoa(InnerView* view, MarginView* viewMargin);
virtual ~ScintillaCocoa();
void RegisterNotifyCallback(intptr_t windowid, SciNotifyFunc callback);
sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
ScintillaView* TopContainer();
+ NSScrollView* ScrollContainer();
InnerView* ContentView();
bool SyncPaint(void* gc, PRectangle rc);
bool Draw(NSRect rect, CGContextRef gc);
+ void PaintMargin(NSRect aRect);
virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
void SetTicking(bool on);
@@ -156,9 +162,10 @@ public:
void SetVerticalScrollPos();
void SetHorizontalScrollPos();
bool ModifyScrollBars(int nMax, int nPage);
+ bool SetScrollingSize(void);
void Resize();
- void DoScroll(float position, NSScrollerPart part, bool horizontal);
-
+ void UpdateForScroll();
+
// Notifications for the owner.
void NotifyChange();
void NotifyFocus(bool focus);
diff --git a/cocoa/ScintillaCocoa.mm b/cocoa/ScintillaCocoa.mm
index 56de63d9c..31b382f3e 100644
--- a/cocoa/ScintillaCocoa.mm
+++ b/cocoa/ScintillaCocoa.mm
@@ -379,15 +379,17 @@ const CGFloat paddingHighlightY = 2;
//----------------- ScintillaCocoa -----------------------------------------------------------------
-ScintillaCocoa::ScintillaCocoa(InnerView* view)
+ScintillaCocoa::ScintillaCocoa(InnerView* view, MarginView* viewMargin)
{
+ vs.marginInside = false;
wMain = view; // Don't retain since we're owned by view, which would cause a cycle
+ wMargin = viewMargin;
timerTarget = [[TimerTarget alloc] init: this];
lastMouseEvent = NULL;
notifyObj = NULL;
notifyProc = NULL;
capturedMouse = false;
-
+ enteredSetScrollingSize = false;
scrollSpeed = 1;
scrollTicks = 2000;
tickTimer = NULL;
@@ -413,7 +415,7 @@ ScintillaCocoa::~ScintillaCocoa()
void ScintillaCocoa::Initialise()
{
Scintilla_LinkLexers();
-
+
// Tell Scintilla not to buffer: Quartz buffers drawing for us.
WndProc(SCI_SETBUFFEREDDRAW, 0, 0);
@@ -678,7 +680,17 @@ void ScintillaCocoa::CancelModes() {
ScintillaView* ScintillaCocoa::TopContainer()
{
NSView* container = static_cast<NSView*>(wMain.GetID());
- return static_cast<ScintillaView*>([container superview]);
+ return static_cast<ScintillaView*>([[[container superview] superview] superview]);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Helper function to get the scrolling view.
+ */
+NSScrollView* ScintillaCocoa::ScrollContainer() {
+ NSView* container = static_cast<NSView*>(wMain.GetID());
+ return static_cast<NSScrollView*>([[container superview] superview]);
}
//--------------------------------------------------------------------------------------------------
@@ -694,28 +706,43 @@ InnerView* ScintillaCocoa::ContentView()
//--------------------------------------------------------------------------------------------------
/**
+ * Return the top left visible point relative to the origin point of the whole document.
+ */
+Scintilla::Point ScintillaCocoa::GetVisibleOriginInMain()
+{
+ NSScrollView *scrollView = ScrollContainer();
+ NSRect contentRect = [[scrollView contentView] bounds];
+ return Point(contentRect.origin.x, contentRect.origin.y);
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
* Instead of returning the size of the inner view we have to return the visible part of it
* in order to make scrolling working properly.
+ * The returned value is in document coordinates.
*/
PRectangle ScintillaCocoa::GetClientRectangle()
{
NSView* host = ContentView();
- NSSize size = [host frame].size;
- return PRectangle(0, 0, size.width, size.height);
+ NSSize size = [[host superview] frame].size;
+ Point origin = GetVisibleOriginInMain();
+ return PRectangle(origin.x, origin.y, origin.x+size.width, origin.y + size.height);
}
//--------------------------------------------------------------------------------------------------
/**
* Converts the given point from base coordinates to local coordinates and at the same time into
- * a native Point structure. Base coordinates are used for the top window used in the view hierarchy.
+ * a native Point structure. Base coordinates are used for the top window used in the view hierarchy.
+ * Returned value is in view coordinates.
*/
Scintilla::Point ScintillaCocoa::ConvertPoint(NSPoint point)
{
NSView* container = ContentView();
NSPoint result = [container convertPoint: point fromView: nil];
-
- return Point(result.x, result.y);
+ Scintilla::Point ptOrigin = GetVisibleOriginInMain();
+ return Point(result.x - ptOrigin.x, result.y - ptOrigin.y);
}
//--------------------------------------------------------------------------------------------------
@@ -752,10 +779,10 @@ sptr_t scintilla_send_message(void* sci, unsigned int iMessage, uptr_t wParam, s
//--------------------------------------------------------------------------------------------------
-/**
+/**
* That's our fake window procedure. On Windows each window has a dedicated procedure to handle
* commands (also used to synchronize UI and background threads), which is not the case in Cocoa.
- *
+ *
* Messages handled here are almost solely for special commands of the backend. Everything which
* would be sytem messages on Windows (e.g. for key down, mouse move etc.) are handled by
* directly calling appropriate handlers.
@@ -771,14 +798,14 @@ sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPar
return reinterpret_cast<sptr_t>(this);
case SCI_GRABFOCUS:
- [[ContentView() window] makeFirstResponder:ContentView()];
+ [[ContentView() window] makeFirstResponder:ContentView()];
break;
-
+
case SCI_SETBUFFEREDDRAW:
- // Buffered drawing not supported on Cocoa
+ // Buffered drawing not supported on Cocoa
bufferedDraw = false;
break;
-
+
case SCI_FINDINDICATORSHOW:
ShowFindIndicatorForRange(NSMakeRange(wParam, lParam-wParam), YES);
return 0;
@@ -786,12 +813,12 @@ sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPar
case SCI_FINDINDICATORFLASH:
ShowFindIndicatorForRange(NSMakeRange(wParam, lParam-wParam), NO);
return 0;
-
+
case SCI_FINDINDICATORHIDE:
HideFindIndicator();
return 0;
-
- case WM_UNICHAR:
+
+ case WM_UNICHAR:
// Special case not used normally. Characters passed in this way will be inserted
// regardless of their value or modifier states. That means no command interpretation is
// performed.
@@ -803,7 +830,7 @@ sptr_t ScintillaCocoa::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lPar
return 1;
}
return 0;
-
+
default:
sptr_t r = ScintillaBase::WndProc(iMessage, wParam, lParam);
@@ -1511,51 +1538,31 @@ bool ScintillaCocoa::SyncPaint(void* gc, PRectangle rc)
//--------------------------------------------------------------------------------------------------
/**
- * Scrolls the pixels in the window some number of lines.
- * Invalidates the pixels scrolled into view.
+ * Paint the margin into the MarginView space.
*/
-void ScintillaCocoa::ScrollText(int linesToMove)
+void ScintillaCocoa::PaintMargin(NSRect aRect)
{
- // Move those pixels
- NSView *content = ContentView();
- if ([content layer]) {
- [content setNeedsDisplay: YES];
- MoveFindIndicatorWithBounce(NO);
- return;
- }
-
- [content lockFocus];
- int diff = vs.lineHeight * linesToMove;
- PRectangle textRect = GetTextRectangle();
- // Include margins as they must scroll
- textRect.left = 0;
- NSRect textRectangle = PRectangleToNSRect(textRect);
- NSPoint destPoint = textRectangle.origin;
- destPoint.y += diff;
- NSCopyBits(0, textRectangle, destPoint);
-
- // Paint them nice
- NSRect redrawRectangle = textRectangle;
- if (linesToMove < 0) {
- // Repaint bottom
- redrawRectangle.origin.y = redrawRectangle.origin.y + redrawRectangle.size.height + diff;
- redrawRectangle.size.height = -diff;
- } else {
- // Repaint top
- redrawRectangle.size.height = diff;
- }
-
- [content drawRect: redrawRectangle];
- [content unlockFocus];
+ CGContextRef gc = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
+
+ PRectangle rc = NSRectToPRectangle(aRect);
+ rcPaint = rc;
+ Surface *sw = Surface::Allocate(SC_TECHNOLOGY_DEFAULT);
+ if (sw)
+ {
+ sw->Init(gc, wMargin.GetID());
+ PaintSelMargin(sw, rc);
+ sw->Release();
+ delete sw;
+ }
+}
- // If no flush done here then multiple scrolls will get buffered and screen
- // will only update a few times a second.
- //[[content window] flushWindow];
- // However, doing the flush leads to the caret updating as a separate operation
- // which looks bad when scrolling by holding down the down arrow key.
+//--------------------------------------------------------------------------------------------------
- // Could invalidate instead of synchronous draw but that may not be as smooth
- //[content setNeedsDisplayInRect: redrawRectangle];
+/**
+ * ScrollText is empty because scrolling is handled by the NSScrollView.
+ */
+void ScintillaCocoa::ScrollText(int linesToMove)
+{
}
//--------------------------------------------------------------------------------------------------
@@ -1565,13 +1572,13 @@ void ScintillaCocoa::ScrollText(int linesToMove)
*/
void ScintillaCocoa::SetVerticalScrollPos()
{
- ScintillaView* topContainer = TopContainer();
-
- // Convert absolute coordinate into the range [0..1]. Keep in mind that the visible area
- // does *not* belong to the scroll range.
- int maxScrollPos = MaxScrollPos();
- float relativePosition = (maxScrollPos > 0) ? ((float) topLine / maxScrollPos) : 0.0f;
- [topContainer setVerticalScrollPosition: relativePosition];
+ NSScrollView *scrollView = ScrollContainer();
+ if (scrollView) {
+ NSClipView *clipView = [scrollView contentView];
+ NSRect contentRect = [clipView bounds];
+ [clipView scrollToPoint: NSMakePoint(contentRect.origin.x, topLine * vs.lineHeight)];
+ [scrollView reflectScrolledClipView:clipView];
+ }
}
//--------------------------------------------------------------------------------------------------
@@ -1581,18 +1588,20 @@ void ScintillaCocoa::SetVerticalScrollPos()
*/
void ScintillaCocoa::SetHorizontalScrollPos()
{
- ScintillaView* topContainer = TopContainer();
PRectangle textRect = GetTextRectangle();
-
- // Convert absolute coordinate into the range [0..1]. Keep in mind that the visible area
- // does *not* belong to the scroll range.
+
int maxXOffset = scrollWidth - textRect.Width();
if (maxXOffset < 0)
maxXOffset = 0;
if (xOffset > maxXOffset)
xOffset = maxXOffset;
- float relativePosition = (float) xOffset / maxXOffset;
- [topContainer setHorizontalScrollPosition: relativePosition];
+ NSScrollView *scrollView = ScrollContainer();
+ if (scrollView) {
+ NSClipView * clipView = [scrollView contentView];
+ NSRect contentRect = [clipView bounds];
+ [clipView scrollToPoint: NSMakePoint(xOffset, contentRect.origin.y)];
+ [scrollView reflectScrolledClipView:clipView];
+ }
MoveFindIndicatorWithBounce(NO);
}
@@ -1600,6 +1609,7 @@ void ScintillaCocoa::SetHorizontalScrollPos()
/**
* Used to adjust both scrollers to reflect the current scroll range and position in the editor.
+ * Arguments no longer used as NSScrollView handles details of scroll bar sizes.
*
* @param nMax Number of lines in the editor.
* @param nPage Number of lines per scroll page.
@@ -1607,105 +1617,66 @@ void ScintillaCocoa::SetHorizontalScrollPos()
*/
bool ScintillaCocoa::ModifyScrollBars(int nMax, int nPage)
{
-#pragma unused(nPage)
- // Input values are given in lines, not pixels, so we have to convert.
- int lineHeight = static_cast<int>(WndProc(SCI_TEXTHEIGHT, 0, 0));
- PRectangle bounds = GetTextRectangle();
- ScintillaView* topContainer = TopContainer();
-
- // Set page size to the same value as the scroll range to hide the scrollbar.
- int scrollRange = lineHeight * (nMax + 1); // +1 because the caller subtracted one.
- int pageSize;
- if (verticalScrollBarVisible)
- pageSize = bounds.Height();
- else
- pageSize = scrollRange;
- bool verticalChange = [topContainer setVerticalScrollRange: scrollRange page: pageSize];
-
- scrollRange = scrollWidth;
- if (horizontalScrollBarVisible)
- pageSize = bounds.Width();
- else
- pageSize = scrollRange;
- bool horizontalChange = [topContainer setHorizontalScrollRange: scrollRange page: pageSize];
-
- MoveFindIndicatorWithBounce(NO);
-
- return verticalChange || horizontalChange;
+#pragma unused(nMax, nPage)
+ return SetScrollingSize();
+}
+
+bool ScintillaCocoa::SetScrollingSize(void) {
+ bool changes = false;
+ InnerView *inner = ContentView();
+ if (!enteredSetScrollingSize) {
+ enteredSetScrollingSize = true;
+ NSScrollView *scrollView = ScrollContainer();
+ NSClipView *clipView = [ScrollContainer() contentView];
+ NSRect clipRect = [clipView bounds];
+ int docHeight = (cs.LinesDisplayed()+1) * vs.lineHeight;
+ if (!endAtLastLine)
+ docHeight += (int([scrollView bounds].size.height / vs.lineHeight)-3) * vs.lineHeight;
+ // Allow extra space so that last scroll position places whole line at top
+ int clipExtra = int(clipRect.size.height) % vs.lineHeight;
+ docHeight += clipExtra;
+ // Ensure all of clipRect covered by Scintilla drawing
+ if (docHeight < clipRect.size.height)
+ docHeight = clipRect.size.height;
+ int docWidth = scrollWidth;
+ bool showHorizontalScroll = horizontalScrollBarVisible &&
+ (wrapState == eWrapNone);
+ if (!showHorizontalScroll)
+ docWidth = clipRect.size.width;
+ NSRect contentRect = {0, 0, docWidth, docHeight};
+ NSRect contentRectNow = [inner frame];
+ changes = (contentRect.size.width != contentRectNow.size.width) ||
+ (contentRect.size.height != contentRectNow.size.height);
+ if (changes) {
+ [inner setFrame: contentRect];
+ }
+ [scrollView setHasVerticalScroller: verticalScrollBarVisible];
+ [scrollView setHasHorizontalScroller: showHorizontalScroll];
+ SetVerticalScrollPos();
+ enteredSetScrollingSize = false;
+ }
+ [inner.owner setMarginWidth: vs.fixedColumnWidth];
+ return changes;
}
//--------------------------------------------------------------------------------------------------
void ScintillaCocoa::Resize()
{
+ SetScrollingSize();
ChangeSize();
}
//--------------------------------------------------------------------------------------------------
/**
- * Called by the frontend control when the user manipulates one of the scrollers.
- *
- * @param position The relative position of the scroller in the range of [0..1].
- * @param part Specifies which part was clicked on by the user, so we can handle thumb tracking
- * as well as page and line scrolling.
- * @param horizontal True if the horizontal scroller was hit, otherwise false.
+ * Update fields to match scroll position after receiving a notification that the user has scrolled.
*/
-void ScintillaCocoa::DoScroll(float position, NSScrollerPart part, bool horizontal)
-{
- // If the given scroller part is not the knob (or knob slot) then the given position is not yet
- // current and we have to update it.
- if (horizontal)
- {
- // Horizontal offset is given in pixels.
- PRectangle textRect = GetTextRectangle();
- int offset = (int) (position * (scrollWidth - textRect.Width()));
- int smallChange = (int) (textRect.Width() / 30);
- if (smallChange < 5)
- smallChange = 5;
- switch (part)
- {
- case NSScrollerDecrementLine:
- offset -= smallChange;
- break;
- case NSScrollerDecrementPage:
- offset -= textRect.Width();
- break;
- case NSScrollerIncrementLine:
- offset += smallChange;
- break;
- case NSScrollerIncrementPage:
- offset += textRect.Width();
- break;
- };
- HorizontalScrollTo(offset);
- }
- else
- {
- // VerticalScrolling is by line. If the user is scrolling using the knob we can directly
- // set the new scroll position. Otherwise we have to compute it first.
- if (part == NSScrollerKnob)
- ScrollTo(position * MaxScrollPos(), false);
- else
- {
- switch (part)
- {
- case NSScrollerDecrementLine:
- ScrollTo(topLine - 1, true);
- break;
- case NSScrollerDecrementPage:
- ScrollTo(topLine - LinesOnScreen(), true);
- break;
- case NSScrollerIncrementLine:
- ScrollTo(topLine + 1, true);
- break;
- case NSScrollerIncrementPage:
- ScrollTo(topLine + LinesOnScreen(), true);
- break;
- };
-
- }
- }
+void ScintillaCocoa::UpdateForScroll() {
+ Point ptOrigin = GetVisibleOriginInMain();
+ xOffset = ptOrigin.x;
+ int newTop = Platform::Minimum(ptOrigin.y / vs.lineHeight, MaxScrollPos());
+ SetTopLine(newTop);
}
//--------------------------------------------------------------------------------------------------
@@ -1980,7 +1951,7 @@ void ScintillaCocoa::MouseDown(NSEvent* event)
void ScintillaCocoa::MouseMove(NSEvent* event)
{
lastMouseEvent = event;
-
+
ButtonMove(ConvertPoint([event locationInWindow]));
}
@@ -1999,11 +1970,8 @@ void ScintillaCocoa::MouseUp(NSEvent* event)
void ScintillaCocoa::MouseWheel(NSEvent* event)
{
bool command = ([event modifierFlags] & NSCommandKeyMask) != 0;
- int dX = 0;
int dY = 0;
- dX = 10 * [event deltaX]; // Arbitrary scale factor.
-
// In order to make scrolling with larger offset smoother we scroll less lines the larger the
// delta value is.
if ([event deltaY] < 0)
@@ -2022,8 +1990,6 @@ void ScintillaCocoa::MouseWheel(NSEvent* event)
}
else
{
- HorizontalScrollTo(xOffset - dX);
- ScrollTo(topLine - dY, true);
}
}
@@ -2152,6 +2118,8 @@ void ScintillaCocoa::MoveFindIndicatorWithBounce(BOOL bounce)
CGPoint ptText = CGPointMake(
WndProc(SCI_POINTXFROMPOSITION, 0, layerFindIndicator.positionFind),
WndProc(SCI_POINTYFROMPOSITION, 0, layerFindIndicator.positionFind));
+ ptText.x = ptText.x - vs.fixedColumnWidth + xOffset;
+ ptText.y += topLine * vs.lineHeight;
if (!layerFindIndicator.geometryFlipped)
{
NSView *content = ContentView();
diff --git a/cocoa/ScintillaView.h b/cocoa/ScintillaView.h
index f34ac7e08..87a443f73 100644
--- a/cocoa/ScintillaView.h
+++ b/cocoa/ScintillaView.h
@@ -27,6 +27,24 @@ extern NSString *SCIUpdateUINotification;
@end
/**
+ * MarginView draws line numbers and other margins next to the text view.
+ */
+@interface MarginView : NSRulerView
+{
+@private
+ int marginWidth;
+ ScintillaView *owner;
+ NSMutableArray *currentCursors;
+}
+
+@property (assign) int marginWidth;
+@property (assign) ScintillaView *owner;
+
+- (id)initWithScrollView:(NSScrollView *)aScrollView;
+
+@end
+
+/**
* InnerView is the Cocoa interface to the Scintilla backend. It handles text input and
* provides a canvas for painting the output.
*/
@@ -63,8 +81,8 @@ extern NSString *SCIUpdateUINotification;
// This is the actual content to which the backend renders itself.
InnerView* mContent;
- NSScroller* mHorizontalScroller;
- NSScroller* mVerticalScroller;
+ NSScrollView *scrollView;
+ MarginView *marginView;
CGFloat zoomDelta;
@@ -78,6 +96,7 @@ extern NSString *SCIUpdateUINotification;
@property (nonatomic, readonly) Scintilla::ScintillaCocoa* backend;
@property (nonatomic, assign) id<ScintillaNotificationProtocol> delegate;
+@property (nonatomic, readonly) NSScrollView *scrollView;
- (void) dealloc;
- (void) positionSubViews;
@@ -90,11 +109,7 @@ extern NSString *SCIUpdateUINotification;
- (void) suspendDrawing: (BOOL) suspend;
// Scroller handling
-- (BOOL) setVerticalScrollRange: (int) range page: (int) page;
-- (void) setVerticalScrollPosition: (float) position;
-- (BOOL) setHorizontalScrollRange: (int) range page: (int) page;
-- (void) setHorizontalScrollPosition: (float) position;
-
+- (void) setMarginWidth: (int) width;
- (void) scrollerAction: (id) sender;
- (InnerView*) content;
diff --git a/cocoa/ScintillaView.mm b/cocoa/ScintillaView.mm
index a2ae060d7..9ff84240d 100644
--- a/cocoa/ScintillaView.mm
+++ b/cocoa/ScintillaView.mm
@@ -48,13 +48,112 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
}
+@implementation MarginView
+
+@synthesize marginWidth, owner;
+
+- (id)initWithScrollView:(NSScrollView *)aScrollView
+{
+ self = [super initWithScrollView:aScrollView orientation:NSVerticalRuler];
+ if (self != nil)
+ {
+ owner = nil;
+ marginWidth = 20;
+ currentCursors = [[NSMutableArray arrayWithCapacity:0] retain];
+ for (size_t i=0; i<5; i++)
+ {
+ [currentCursors addObject: [reverseArrowCursor retain]];
+ }
+ [self setClientView:[aScrollView documentView]];
+ }
+ return self;
+}
+
+- (void) dealloc
+{
+ [currentCursors release];
+ [super dealloc];
+}
+
+- (void) setFrame: (NSRect) frame
+{
+ [super setFrame: frame];
+
+ [[self window] invalidateCursorRectsForView: self];
+}
+
+- (CGFloat)requiredThickness
+{
+ return marginWidth;
+}
+
+- (void)drawHashMarksAndLabelsInRect:(NSRect)aRect
+{
+ if (owner) {
+ NSRect contentRect = [[[self scrollView] contentView] bounds];
+ NSRect marginRect = [self bounds];
+ // Ensure paint to bottom of view to avoid glitches
+ if (marginRect.size.height > contentRect.size.height) {
+ // Legacy scroll bar mode leaves a poorly painted corner
+ aRect = marginRect;
+ }
+ owner.backend->PaintMargin(aRect);
+ }
+}
+
+- (void) mouseDown: (NSEvent *) theEvent
+{
+ owner.backend->MouseDown(theEvent);
+}
+
+- (void) mouseDragged: (NSEvent *) theEvent
+{
+ owner.backend->MouseMove(theEvent);
+}
+
+- (void) mouseMoved: (NSEvent *) theEvent
+{
+ owner.backend->MouseMove(theEvent);
+}
+
+- (void) mouseUp: (NSEvent *) theEvent
+{
+ owner.backend->MouseUp(theEvent);
+}
+
+/**
+ * This method is called to give us the opportunity to define our mouse sensitive rectangle.
+ */
+- (void) resetCursorRects
+{
+ [super resetCursorRects];
+
+ int x = 0;
+ NSRect marginRect = [self bounds];
+ size_t co = [currentCursors count];
+ for (size_t i=0; i<co; i++)
+ {
+ int cursType = owner.backend->WndProc(SCI_GETMARGINCURSORN, i, 0);
+ int width =owner.backend->WndProc(SCI_GETMARGINWIDTHN, i, 0);
+ NSCursor *cc = cursorFromEnum(static_cast<Window::Cursor>(cursType));
+ [currentCursors replaceObjectAtIndex:i withObject: cc];
+ marginRect.origin.x = x;
+ marginRect.size.width = width;
+ [self addCursorRect: marginRect cursor: cc];
+ [cc setOnMouseEntered: YES];
+ x += width;
+ }
+}
+
+@end
+
@implementation InnerView
@synthesize owner = mOwner;
//--------------------------------------------------------------------------------------------------
-- (NSView*) initWithFrame: (NSRect) frame
+- (NSView*) initWithFrame: (NSRect) frame
{
self = [super initWithFrame: frame];
@@ -449,9 +548,35 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
//--------------------------------------------------------------------------------------------------
+/**
+ * Mouse wheel with command key magnifies text.
+ */
- (void) scrollWheel: (NSEvent *) theEvent
{
- mOwner.backend->MouseWheel(theEvent);
+ if (([theEvent modifierFlags] & NSCommandKeyMask) != 0) {
+ mOwner.backend->MouseWheel(theEvent);
+ } else {
+ [super scrollWheel:theEvent];
+ }
+}
+
+//--------------------------------------------------------------------------------------------------
+
+/**
+ * Ensure scrolling is aligned to whole lines instead of starting part-way through a line
+ */
+- (NSRect)adjustScroll:(NSRect)proposedVisibleRect
+{
+ NSRect rc = proposedVisibleRect;
+ // Snap to lines
+ NSRect contentRect = [self bounds];
+ if ((rc.origin.y > 0) && (NSMaxY(rc) < contentRect.size.height)) {
+ // Only snap for positions inside the document - allow outside
+ // for overshoot.
+ int lineHeight = mOwner.backend->WndProc(SCI_TEXTHEIGHT, 0, 0);
+ rc.origin.y = roundf(rc.origin.y / lineHeight) * lineHeight;
+ }
+ return rc;
}
//--------------------------------------------------------------------------------------------------
@@ -632,6 +757,7 @@ static NSCursor *cursorFromEnum(Window::Cursor cursor)
@synthesize backend = mBackend;
@synthesize delegate = mDelegate;
+@synthesize scrollView;
/**
* ScintillaView is a composite control made from an NSView and an embedded NSView that is
@@ -843,27 +969,32 @@ static void notification(intptr_t windowid, unsigned int iMessage, uintptr_t wPa
if (self)
{
mContent = [[[InnerView alloc] init] autorelease];
- mBackend = new ScintillaCocoa(mContent);
mContent.owner = self;
- [self addSubview: mContent];
-
+
// Initialize the scrollers but don't show them yet.
// Pick an arbitrary size, just to make NSScroller selecting the proper scroller direction
// (horizontal or vertical).
NSRect scrollerRect = NSMakeRect(0, 0, 100, 10);
- mHorizontalScroller = [[[NSScroller alloc] initWithFrame: scrollerRect] autorelease];
- [mHorizontalScroller setHidden: YES];
- [mHorizontalScroller setTarget: self];
- [mHorizontalScroller setAction: @selector(scrollerAction:)];
- [self addSubview: mHorizontalScroller];
-
- scrollerRect.size = NSMakeSize(10, 100);
- mVerticalScroller = [[[NSScroller alloc] initWithFrame: scrollerRect] autorelease];
- [mVerticalScroller setHidden: YES];
- [mVerticalScroller setTarget: self];
- [mVerticalScroller setAction: @selector(scrollerAction:)];
- [self addSubview: mVerticalScroller];
+ scrollView = [[[NSScrollView alloc] initWithFrame: scrollerRect] autorelease];
+ [scrollView setDocumentView: mContent];
+ [scrollView setHasVerticalScroller:YES];
+ [scrollView setHasHorizontalScroller:YES];
+ [scrollView setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable];
+ //[scrollView setScrollerStyle:NSScrollerStyleLegacy];
+ //[scrollView setScrollerKnobStyle:NSScrollerKnobStyleDark];
+ //[scrollView setHorizontalScrollElasticity:NSScrollElasticityNone];
+ [self addSubview: scrollView];
+
+ marginView = [[MarginView alloc] initWithScrollView:scrollView];
+ marginView.owner = self;
+ [marginView setRuleThickness:[marginView requiredThickness]];
+ [scrollView setVerticalRulerView:marginView];
+ [scrollView setHasHorizontalRuler:NO];
+ [scrollView setHasVerticalRuler:YES];
+ [scrollView setRulersVisible:YES];
+ mBackend = new ScintillaCocoa(mContent, marginView);
+
// Establish a connection from the back end to this container so we can handle situations
// which require our attention.
mBackend->RegisterNotifyCallback(nil, notification);
@@ -885,6 +1016,12 @@ static void notification(intptr_t windowid, unsigned int iMessage, uintptr_t wPa
selector:@selector(applicationDidBecomeActive:)
name:NSApplicationDidBecomeActiveNotification
object:nil];
+
+ [[scrollView contentView] setPostsBoundsChangedNotifications:YES];
+ [center addObserver:self
+ selector:@selector(scrollerAction:)
+ name:NSViewBoundsDidChangeNotification
+ object:[scrollView contentView]];
}
return self;
}
@@ -934,193 +1071,47 @@ static void notification(intptr_t windowid, unsigned int iMessage, uintptr_t wPa
int scrollerWidth = [NSScroller scrollerWidth];
NSSize size = [self frame].size;
- NSRect hScrollerRect = {0, 0, size.width, static_cast<CGFloat>(scrollerWidth)};
- NSRect vScrollerRect = {size.width - scrollerWidth, 0, static_cast<CGFloat>(scrollerWidth), size.height};
NSRect barFrame = {0, size.height - scrollerWidth, size.width, static_cast<CGFloat>(scrollerWidth)};
BOOL infoBarVisible = mInfoBar != nil && ![mInfoBar isHidden];
-
+
// Horizontal offset of the content. Almost always 0 unless the vertical scroller
// is on the left side.
int contentX = 0;
-
- // Vertical scroller frame calculation.
- if (![mVerticalScroller isHidden])
- {
- // Consider user settings (left vs right vertical scrollbar).
- BOOL isLeft = [[[NSUserDefaults standardUserDefaults] stringForKey: @"NSScrollerPosition"]
- isEqualToString: @"left"];
- if (isLeft)
- {
- vScrollerRect.origin.x = 0;
- hScrollerRect.origin.x = scrollerWidth;
- contentX = scrollerWidth;
- };
-
- size.width -= scrollerWidth;
- hScrollerRect.size.width -= scrollerWidth;
- }
-
- // Same for horizontal scroller.
- if (![mHorizontalScroller isHidden])
- {
- // Make room for the h-scroller.
- size.height -= scrollerWidth;
- vScrollerRect.size.height -= scrollerWidth;
- vScrollerRect.origin.y += scrollerWidth;
- };
-
+ NSRect scrollRect = {contentX, 0, size.width, size.height};
+
// Info bar frame.
if (infoBarVisible)
{
+ scrollRect.size.height -= scrollerWidth;
// Initial value already is as if the bar is at top.
- if (mInfoBarAtTop)
+ if (!mInfoBarAtTop)
{
- vScrollerRect.size.height -= scrollerWidth;
- size.height -= scrollerWidth;
- }
- else
- {
- // Layout info bar and h-scroller side by side in a friendly manner.
- int nativeWidth = mInitialInfoBarWidth;
- int remainingWidth = barFrame.size.width;
-
+ scrollRect.origin.y += scrollerWidth;
barFrame.origin.y = 0;
-
- if ([mHorizontalScroller isHidden])
- {
- // H-scroller is not visible, so take the full space.
- vScrollerRect.origin.y += scrollerWidth;
- vScrollerRect.size.height -= scrollerWidth;
- size.height -= scrollerWidth;
- }
- else
- {
- // If the left offset of the h-scroller is > 0 then the v-scroller is on the left side.
- // In this case we take the full width, otherwise what has been given to the h-scroller
- // and content up to now.
- if (hScrollerRect.origin.x == 0)
- remainingWidth = size.width;
-
- // Note: remainingWidth can become < 0, which hides the scroller.
- remainingWidth -= nativeWidth;
-
- hScrollerRect.origin.x = nativeWidth;
- hScrollerRect.size.width = remainingWidth;
- barFrame.size.width = nativeWidth;
- }
}
}
-
- NSRect contentRect = {static_cast<CGFloat>(contentX), vScrollerRect.origin.y, size.width, size.height};
- [mContent setFrame: contentRect];
-
- if (infoBarVisible)
- [mInfoBar setFrame: barFrame];
- if (![mHorizontalScroller isHidden])
- [mHorizontalScroller setFrame: hScrollerRect];
- if (![mVerticalScroller isHidden])
- [mVerticalScroller setFrame: vScrollerRect];
-}
-//--------------------------------------------------------------------------------------------------
-
-/**
- * Called by the backend to adjust the vertical scroller (range and page).
- *
- * @param range Determines the total size of the scroll area used in the editor.
- * @param page Determines how many pixels a page is.
- * @result Returns YES if anything changed, otherwise NO.
- */
-- (BOOL) setVerticalScrollRange: (int) range page: (int) page
-{
- BOOL result = NO;
- BOOL hideScroller = page >= range;
-
- if ([mVerticalScroller isHidden] != hideScroller)
- {
- result = YES;
- [mVerticalScroller setHidden: hideScroller];
- if (!hideScroller)
- [mVerticalScroller setFloatValue: 0];
- [self positionSubViews];
+ if (!NSEqualRects([scrollView frame], scrollRect)) {
+ [scrollView setFrame: scrollRect];
+ mBackend->Resize();
}
-
- if (!hideScroller)
- {
- [mVerticalScroller setEnabled: YES];
-
- CGFloat currentProportion = [mVerticalScroller knobProportion];
- CGFloat newProportion = page / (CGFloat) range;
- if (currentProportion != newProportion)
- {
- result = YES;
- [mVerticalScroller setKnobProportion: newProportion];
- }
- }
-
- return result;
-}
-//--------------------------------------------------------------------------------------------------
-
-/**
- * Used to set the position of the vertical scroll thumb.
- *
- * @param position The relative position in the range [0..1];
- */
-- (void) setVerticalScrollPosition: (float) position
-{
- [mVerticalScroller setFloatValue: position];
+ if (infoBarVisible)
+ [mInfoBar setFrame: barFrame];
}
//--------------------------------------------------------------------------------------------------
/**
- * Called by the backend to adjust the horizontal scroller (range and page).
- *
- * @param range Determines the total size of the scroll area used in the editor.
- * @param page Determines how many pixels a page is.
- * @result Returns YES if anything changed, otherwise NO.
+ * Set the width of the margin.
*/
-- (BOOL) setHorizontalScrollRange: (int) range page: (int) page
+- (void) setMarginWidth: (int) width
{
- BOOL result = NO;
- BOOL hideScroller = (page >= range) ||
- (mBackend->WndProc(SCI_GETWRAPMODE, 0, 0) != SC_WRAP_NONE);
-
- if ([mHorizontalScroller isHidden] != hideScroller)
+ if (marginView.ruleThickness != width)
{
- result = YES;
- [mHorizontalScroller setHidden: hideScroller];
- [self positionSubViews];
+ marginView.marginWidth = width;
+ [marginView setRuleThickness:[marginView requiredThickness]];
}
-
- if (!hideScroller)
- {
- [mHorizontalScroller setEnabled: YES];
-
- CGFloat currentProportion = [mHorizontalScroller knobProportion];
- CGFloat newProportion = page / (CGFloat) range;
- if (currentProportion != newProportion)
- {
- result = YES;
- [mHorizontalScroller setKnobProportion: newProportion];
- }
- }
-
- return result;
-}
-
-//--------------------------------------------------------------------------------------------------
-
-/**
- * Used to set the position of the vertical scroll thumb.
- *
- * @param position The relative position in the range [0..1];
- */
-- (void) setHorizontalScrollPosition: (float) position
-{
- [mHorizontalScroller setFloatValue: position];
}
//--------------------------------------------------------------------------------------------------
@@ -1131,8 +1122,7 @@ static void notification(intptr_t windowid, unsigned int iMessage, uintptr_t wPa
*/
- (void) scrollerAction: (id) sender
{
- float position = [sender doubleValue];
- mBackend->DoScroll(position, [sender hitPart], sender == mHorizontalScroller);
+ mBackend->UpdateForScroll();
}
//--------------------------------------------------------------------------------------------------
diff --git a/src/Editor.cxx b/src/Editor.cxx
index 4f00e501a..8b9084fc5 100644
--- a/src/Editor.cxx
+++ b/src/Editor.cxx
@@ -175,6 +175,7 @@ Editor::Editor() {
pixmapLine = 0;
pixmapSelMargin = 0;
pixmapSelPattern = 0;
+ pixmapSelPatternOffset1 = 0;
pixmapIndentGuide = 0;
pixmapIndentGuideHighlight = 0;
@@ -250,6 +251,8 @@ void Editor::DropGraphics(bool freeObjects) {
pixmapSelMargin = 0;
delete pixmapSelPattern;
pixmapSelPattern = 0;
+ delete pixmapSelPatternOffset1;
+ pixmapSelPatternOffset1 = 0;
delete pixmapIndentGuide;
pixmapIndentGuide = 0;
delete pixmapIndentGuideHighlight;
@@ -261,6 +264,8 @@ void Editor::DropGraphics(bool freeObjects) {
pixmapSelMargin->Release();
if (pixmapSelPattern)
pixmapSelPattern->Release();
+ if (pixmapSelPatternOffset1)
+ pixmapSelPatternOffset1->Release();
if (pixmapIndentGuide)
pixmapIndentGuide->Release();
if (pixmapIndentGuideHighlight)
@@ -275,6 +280,8 @@ void Editor::AllocateGraphics() {
pixmapSelMargin = Surface::Allocate(technology);
if (!pixmapSelPattern)
pixmapSelPattern = Surface::Allocate(technology);
+ if (!pixmapSelPatternOffset1)
+ pixmapSelPatternOffset1 = Surface::Allocate(technology);
if (!pixmapIndentGuide)
pixmapIndentGuide = Surface::Allocate(technology);
if (!pixmapIndentGuideHighlight)
@@ -308,13 +315,37 @@ void Editor::RefreshStyleData() {
}
}
+Point Editor::GetVisibleOriginInMain() {
+ return Point(0,0);
+}
+
+Point Editor::DocumentPointFromView(Point ptView) {
+ Point ptDocument = ptView;
+ if (wMargin.GetID()) {
+ Point ptOrigin = GetVisibleOriginInMain();
+ ptDocument.x += ptOrigin.x;
+ ptDocument.y += ptOrigin.y;
+ } else {
+ ptDocument.x += xOffset;
+ ptDocument.y += topLine * vs.lineHeight;
+ }
+ return ptDocument;
+}
+
+int Editor::TopLineOfMain() {
+ if (wMargin.GetID())
+ return 0;
+ else
+ return topLine;
+}
+
PRectangle Editor::GetClientRectangle() {
return wMain.GetClientPosition();
}
PRectangle Editor::GetTextRectangle() {
PRectangle rc = GetClientRectangle();
- rc.left += vs.fixedColumnWidth;
+ rc.left += vs.textStart;
rc.right -= vs.rightMarginWidth;
return rc;
}
@@ -437,7 +468,7 @@ Point Editor::LocationFromPosition(SelectionPosition pos) {
pt.y += vs.lineHeight;
}
}
- pt.x += vs.fixedColumnWidth - xOffset;
+ pt.x += vs.textStart - xOffset;
}
pt.x += pos.VirtualSpace() * vs.styles[ll->EndLineStyle()].spaceWidth;
return pt;
@@ -449,12 +480,12 @@ Point Editor::LocationFromPosition(int pos) {
int Editor::XFromPosition(int pos) {
Point pt = LocationFromPosition(pos);
- return pt.x - vs.fixedColumnWidth + xOffset;
+ return pt.x - vs.textStart + xOffset;
}
int Editor::XFromPosition(SelectionPosition sp) {
Point pt = LocationFromPosition(sp);
- return pt.x - vs.fixedColumnWidth + xOffset;
+ return pt.x - vs.textStart + xOffset;
}
int Editor::LineFromLocation(Point pt) {
@@ -462,7 +493,7 @@ int Editor::LineFromLocation(Point pt) {
}
void Editor::SetTopLine(int topLineNew) {
- if (topLine != topLineNew) {
+ if ((topLine != topLineNew) && (topLineNew >= 0)) {
topLine = topLineNew;
ContainerNeedsUpdate(SC_UPDATE_V_SCROLL);
}
@@ -475,13 +506,14 @@ SelectionPosition Editor::SPositionFromLocation(Point pt, bool canReturnInvalid,
PRectangle rcClient = GetTextRectangle();
if (!rcClient.Contains(pt))
return SelectionPosition(INVALID_POSITION);
- if (pt.x < vs.fixedColumnWidth)
+ if (pt.x < vs.textStart)
return SelectionPosition(INVALID_POSITION);
if (pt.y < 0)
return SelectionPosition(INVALID_POSITION);
}
- pt.x = pt.x - vs.fixedColumnWidth + xOffset;
- int visibleLine = floor(pt.y / vs.lineHeight) + topLine;
+ pt = DocumentPointFromView(pt);
+ pt.x = pt.x - vs.textStart;
+ int visibleLine = floor(pt.y / vs.lineHeight);
if (!canReturnInvalid && (visibleLine < 0))
visibleLine = 0;
int lineDoc = cs.DocFromDisplay(visibleLine);
@@ -620,6 +652,8 @@ void Editor::Redraw() {
//Platform::DebugPrintf("Redraw all\n");
PRectangle rcClient = GetClientRectangle();
wMain.InvalidateRectangle(rcClient);
+ if (wMargin.GetID())
+ wMargin.InvalidateAll();
//wMain.InvalidateAll();
}
@@ -629,7 +663,7 @@ void Editor::RedrawSelMargin(int line, bool allAfter) {
Redraw();
} else {
PRectangle rcSelMargin = GetClientRectangle();
- rcSelMargin.right = vs.fixedColumnWidth;
+ rcSelMargin.right = rcSelMargin.left + vs.fixedColumnWidth;
if (line != -1) {
int position = pdoc->LineStart(line);
PRectangle rcLine = RectangleFromRange(position, position);
@@ -649,7 +683,13 @@ void Editor::RedrawSelMargin(int line, bool allAfter) {
if (!allAfter)
rcSelMargin.bottom = rcLine.bottom;
}
- wMain.InvalidateRectangle(rcSelMargin);
+ if (wMargin.GetID()) {
+ Point ptOrigin = GetVisibleOriginInMain();
+ rcSelMargin.Move(-ptOrigin.x, -ptOrigin.y);
+ wMargin.InvalidateRectangle(rcSelMargin);
+ } else {
+ wMain.InvalidateRectangle(rcSelMargin);
+ }
}
}
}
@@ -667,15 +707,12 @@ PRectangle Editor::RectangleFromRange(int start, int end) {
PRectangle rcClient = GetTextRectangle();
PRectangle rc;
const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
- rc.left = vs.fixedColumnWidth - leftTextOverlap;
- rc.top = (minLine - topLine) * vs.lineHeight;
- if (rc.top < 0)
- rc.top = 0;
+ rc.left = vs.textStart - leftTextOverlap;
+ rc.top = (minLine - TopLineOfMain()) * vs.lineHeight;
+ if (rc.top < rcClient.top)
+ rc.top = rcClient.top;
rc.right = rcClient.right;
- rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
- // Ensure PRectangle is within 16 bit space
- rc.top = Platform::Clamp(rc.top, -32000, 32000);
- rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
+ rc.bottom = (maxLine - TopLineOfMain() + 1) * vs.lineHeight;
return rc;
}
@@ -1199,7 +1236,9 @@ slop | strict | jumps | even | Caret can go to the margin | When
Editor::XYScrollPosition Editor::XYScrollToMakeVisible(const bool useMargin, const bool vert, const bool horiz) {
PRectangle rcClient = GetTextRectangle();
const SelectionPosition posCaret = posDrag.IsValid() ? posDrag : sel.RangeMain().caret;
- const Point pt = LocationFromPosition(posCaret);
+ Point pt = LocationFromPosition(posCaret);
+ const Point ptOrigin = GetVisibleOriginInMain();
+ pt.x += ptOrigin.x;
const Point ptBottomCaret(pt.x, pt.y + vs.lineHeight - 1);
const int lineCaret = DisplayFromPosition(posCaret.Position());
@@ -1543,8 +1582,8 @@ bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
int lineDocTop = cs.DocFromDisplay(topLine);
int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
PRectangle rcTextArea = GetClientRectangle();
- rcTextArea.left = vs.fixedColumnWidth;
- rcTextArea.right -= vs.rightMarginWidth;
+ rcTextArea.left = vs.textStart;
+ rcTextArea.right -= vs.textStart;
wrapWidth = rcTextArea.Width();
RefreshStyleData();
AutoSurface surface(this);
@@ -1754,7 +1793,12 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
if (vs.fixedColumnWidth == 0)
return;
+ RefreshPixMaps(surfWindow);
+
PRectangle rcMargin = GetClientRectangle();
+ Point ptOrigin = GetVisibleOriginInMain();
+ rcMargin.Move(0, -ptOrigin.y);
+ rcMargin.left = 0;
rcMargin.right = vs.fixedColumnWidth;
if (!rc.Intersects(rcMargin))
@@ -1783,10 +1827,14 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
- if (vs.ms[margin].mask & SC_MASK_FOLDERS)
+ if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
// Required because of special way brush is created for selection margin
- surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
- else {
+ // Ensure patterns line up when scrolling with separate margin view
+ // by choosing correctly aligned variant.
+ bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
+ surface->FillRectangle(rcSelMargin,
+ invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
+ } else {
ColourDesired colour;
switch (vs.ms[margin].style) {
case SC_MARGIN_BACK:
@@ -1805,9 +1853,9 @@ void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
}
- const int lineStartPaint = rcMargin.top / vs.lineHeight;
- int visibleLine = topLine + lineStartPaint;
- int yposScreen = lineStartPaint * vs.lineHeight;
+ const int lineStartPaint = (rcMargin.top + ptOrigin.y) / vs.lineHeight;
+ int visibleLine = TopLineOfMain() + lineStartPaint;
+ int yposScreen = lineStartPaint * vs.lineHeight - ptOrigin.y;
// Work out whether the top line is whitespace located after a
// lessening of fold level which implies a 'fold tail' but which should not
// be displayed until the last of a sequence of whitespace.
@@ -3299,6 +3347,7 @@ void Editor::RefreshPixMaps(Surface *surfaceWindow) {
if (!pixmapSelPattern->Initialised()) {
const int patternSize = 8;
pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
+ pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
// This complex procedure is to reproduce the checkerboard dithered pattern used by windows
// for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
// way between the chrome colour and the chrome highlight colour making a nice transition
@@ -3325,10 +3374,12 @@ void Editor::RefreshPixMaps(Surface *surfaceWindow) {
}
pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
+ pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
for (int y = 0; y < patternSize; y++) {
for (int x = y % 2; x < patternSize; x+=2) {
PRectangle rcPixel(x, y, x+1, y+1);
pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
+ pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
}
}
}
@@ -3456,12 +3507,13 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
StyleToPositionInView(PositionAfterArea(rcArea));
PRectangle rcClient = GetClientRectangle();
+ Point ptOrigin = GetVisibleOriginInMain();
//Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
// rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
int screenLinePaintFirst = rcArea.top / vs.lineHeight;
- int xStart = vs.fixedColumnWidth - xOffset;
+ int xStart = vs.textStart - xOffset + ptOrigin.x;
int ypos = 0;
if (!bufferedDraw)
ypos += screenLinePaintFirst * vs.lineHeight;
@@ -3493,12 +3545,20 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
surfaceWindow->SetClip(rcArea);
if (paintState != paintAbandoned) {
- PaintSelMargin(surfaceWindow, rcArea);
-
- PRectangle rcRightMargin = rcClient;
- rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
- if (rcArea.Intersects(rcRightMargin)) {
- surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
+ if (vs.marginInside) {
+ PaintSelMargin(surfaceWindow, rcArea);
+ PRectangle rcRightMargin = rcClient;
+ rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
+ if (rcArea.Intersects(rcRightMargin)) {
+ surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back);
+ }
+ } else { // Else separate view so separate paint event but leftMargin included to allow overlap
+ PRectangle rcLeftMargin = rcArea;
+ rcLeftMargin.left = 0;
+ rcLeftMargin.right = rcLeftMargin.left + vs.leftMarginWidth;
+ if (rcArea.Intersects(rcLeftMargin)) {
+ surfaceWindow->FillRectangle(rcLeftMargin, vs.styles[STYLE_DEFAULT].back);
+ }
}
}
@@ -3522,7 +3582,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
const int leftTextOverlap = ((xOffset == 0) && (vs.leftMarginWidth > 0)) ? 1 : 0;
// Do the painting
- if (rcArea.right > vs.fixedColumnWidth - leftTextOverlap) {
+ if (rcArea.right > vs.textStart - leftTextOverlap) {
Surface *surface = surfaceWindow;
if (bufferedDraw) {
@@ -3532,7 +3592,7 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
surface->SetUnicodeMode(IsUnicodeMode());
surface->SetDBCSMode(CodePage());
- int visibleLine = topLine + screenLinePaintFirst;
+ int visibleLine = TopLineOfMain() + screenLinePaintFirst;
SelectionPosition posCaret = sel.RangeMain().caret;
if (posDrag.IsValid())
@@ -3540,12 +3600,16 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
int lineCaret = pdoc->LineFromPosition(posCaret.Position());
PRectangle rcTextArea = rcClient;
- rcTextArea.left = vs.fixedColumnWidth;
- rcTextArea.right -= vs.rightMarginWidth;
+ if (vs.marginInside) {
+ rcTextArea.left += vs.textStart;
+ rcTextArea.right -= vs.rightMarginWidth;
+ } else {
+ rcTextArea = rcArea;
+ }
// Remove selection margin from drawing area so text will not be drawn
// on it in unbuffered mode.
- if (!bufferedDraw) {
+ if (!bufferedDraw && vs.marginInside) {
PRectangle rcClipText = rcTextArea;
rcClipText.left -= leftTextOverlap;
surfaceWindow->SetClip(rcClipText);
@@ -3639,8 +3703,8 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
DrawCarets(surface, vs, lineDoc, xStart, rcLine, ll, subLine);
if (bufferedDraw) {
- Point from(vs.fixedColumnWidth-leftTextOverlap, 0);
- PRectangle rcCopyArea(vs.fixedColumnWidth-leftTextOverlap, yposScreen,
+ Point from(vs.textStart-leftTextOverlap, 0);
+ PRectangle rcCopyArea(vs.textStart-leftTextOverlap, yposScreen,
rcClient.right - vs.rightMarginWidth, yposScreen + vs.lineHeight);
surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
}
@@ -3664,10 +3728,10 @@ void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
// durPaint = 0.00000001;
// Right column limit indicator
- PRectangle rcBeyondEOF = rcClient;
- rcBeyondEOF.left = vs.fixedColumnWidth;
- rcBeyondEOF.right = rcBeyondEOF.right - vs.rightMarginWidth;
- rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
+ PRectangle rcBeyondEOF = (vs.marginInside) ? rcClient : rcArea;
+ rcBeyondEOF.left = vs.textStart;
+ rcBeyondEOF.right = rcBeyondEOF.right - ((vs.marginInside) ? vs.rightMarginWidth : 0);
+ rcBeyondEOF.top = (cs.LinesDisplayed() - TopLineOfMain()) * vs.lineHeight;
if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back);
if (vs.edgeState == EDGE_LINE) {
@@ -3937,7 +4001,7 @@ void Editor::ChangeSize() {
SetScrollBars();
if (wrapState != eWrapNone) {
PRectangle rcTextArea = GetClientRectangle();
- rcTextArea.left = vs.fixedColumnWidth;
+ rcTextArea.left = vs.textStart;
rcTextArea.right -= vs.rightMarginWidth;
if (wrapWidth != rcTextArea.Width()) {
NeedWrapping();
@@ -4457,7 +4521,7 @@ void Editor::NotifyIndicatorClick(bool click, int position, bool shift, bool ctr
bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
int marginClicked = -1;
- int x = 0;
+ int x = vs.textStart - vs.fixedColumnWidth;
for (int margin = 0; margin <= SC_MAX_MARGIN; margin++) {
if ((pt.x >= x) && (pt.x < x + vs.ms[margin].width))
marginClicked = margin;
@@ -6111,7 +6175,8 @@ bool Editor::PointInSelMargin(Point pt) {
// Really means: "Point in a margin"
if (vs.fixedColumnWidth > 0) { // There is a margin
PRectangle rcSelMargin = GetClientRectangle();
- rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
+ rcSelMargin.right = vs.textStart - vs.leftMarginWidth;
+ rcSelMargin.left = vs.textStart - vs.fixedColumnWidth;
return rcSelMargin.Contains(pt);
} else {
return false;
@@ -6501,6 +6566,8 @@ void Editor::ButtonMove(Point pt) {
// Autoscroll
PRectangle rcClient = GetClientRectangle();
+ Point ptOrigin = GetVisibleOriginInMain();
+ rcClient.Move(0, -ptOrigin.y);
int lineMove = DisplayFromPosition(movePos.Position());
if (pt.y > rcClient.bottom) {
ScrollTo(lineMove - LinesOnScreen() + 1);
@@ -6700,7 +6767,7 @@ int Editor::PositionAfterArea(PRectangle rcArea) {
// Style to a position within the view. If this causes a change at end of last line then
// affects later lines so style all the viewed text.
void Editor::StyleToPositionInView(Position pos) {
- int endWindow = PositionAfterArea(GetClientRectangle());
+ int endWindow = (vs.marginInside) ? (PositionAfterArea(GetClientRectangle())) : (pdoc->Length());
if (pos > endWindow)
pos = endWindow;
int styleAtEnd = pdoc->StyleAt(pos-1);
@@ -6735,8 +6802,13 @@ bool Editor::PaintContains(PRectangle rc) {
}
bool Editor::PaintContainsMargin() {
+ if (wMargin.GetID()) {
+ // With separate margin view, paint of text view
+ // never contains margin.
+ return false;
+ }
PRectangle rcSelMargin = GetClientRectangle();
- rcSelMargin.right = vs.fixedColumnWidth;
+ rcSelMargin.right = vs.textStart;
return PaintContains(rcSelMargin);
}
@@ -7446,7 +7518,8 @@ sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
return 0;
} else {
Point pt = LocationFromPosition(lParam);
- return pt.x;
+ // Convert to view-relative
+ return pt.x - vs.textStart + vs.fixedColumnWidth;
}
case SCI_POINTYFROMPOSITION:
diff --git a/src/Editor.h b/src/Editor.h
index bf3e26d92..91cbd59a4 100644
--- a/src/Editor.h
+++ b/src/Editor.h
@@ -148,6 +148,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
/** On GTK+, Scintilla is a container widget holding two scroll bars
* whereas on Windows there is just one window with both scroll bars turned on. */
Window wMain; ///< The Scintilla parent window
+ Window wMargin; ///< May be separate when using a scroll view for wMain
/** Style resources may be expensive to allocate so are cached between uses.
* When a style attribute is changed, this cache is flushed. */
@@ -199,6 +200,7 @@ protected: // ScintillaBase subclass needs access to much of Editor
Surface *pixmapLine;
Surface *pixmapSelMargin;
Surface *pixmapSelPattern;
+ Surface *pixmapSelPatternOffset1;
Surface *pixmapIndentGuide;
Surface *pixmapIndentGuideHighlight;
@@ -308,6 +310,11 @@ protected: // ScintillaBase subclass needs access to much of Editor
void DropGraphics(bool freeObjects);
void AllocateGraphics();
+ // The top left visible point in main window coordinates. Will be 0,0 except for
+ // scroll views where it will be equivalent to the current scroll position.
+ virtual Point GetVisibleOriginInMain();
+ Point DocumentPointFromView(Point ptView); // Convert a point from view space to document
+ int TopLineOfMain(); // Return the line at Main's y coordinate 0
virtual PRectangle GetClientRectangle();
PRectangle GetTextRectangle();
diff --git a/src/ViewStyle.cxx b/src/ViewStyle.cxx
index f316d264e..9181d9b24 100644
--- a/src/ViewStyle.cxx
+++ b/src/ViewStyle.cxx
@@ -198,6 +198,8 @@ ViewStyle::ViewStyle(const ViewStyle &source) {
}
maskInLine = source.maskInLine;
fixedColumnWidth = source.fixedColumnWidth;
+ marginInside = source.marginInside;
+ textStart = source.textStart;
zoomLevel = source.zoomLevel;
viewWhitespace = source.viewWhitespace;
whitespaceSize = source.whitespaceSize;
@@ -306,13 +308,15 @@ void ViewStyle::Init(size_t stylesSize_) {
ms[2].style = SC_MARGIN_SYMBOL;
ms[2].width = 0;
ms[2].mask = 0;
- fixedColumnWidth = leftMarginWidth;
+ marginInside = true;
+ fixedColumnWidth = marginInside ? leftMarginWidth : 0;
maskInLine = 0xffffffff;
for (int margin=0; margin <= SC_MAX_MARGIN; margin++) {
fixedColumnWidth += ms[margin].width;
if (ms[margin].width > 0)
maskInLine &= ~ms[margin].mask;
}
+ textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
zoomLevel = 0;
viewWhitespace = wsInvisible;
whitespaceSize = 1;
@@ -387,13 +391,14 @@ void ViewStyle::Refresh(Surface &surface) {
aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
- fixedColumnWidth = leftMarginWidth;
+ fixedColumnWidth = marginInside ? leftMarginWidth : 0;
maskInLine = 0xffffffff;
for (int margin=0; margin <= SC_MAX_MARGIN; margin++) {
fixedColumnWidth += ms[margin].width;
if (ms[margin].width > 0)
maskInLine &= ~ms[margin].mask;
}
+ textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
}
void ViewStyle::AllocStyles(size_t sizeNew) {
diff --git a/src/ViewStyle.h b/src/ViewStyle.h
index ddcc64312..bd26613cb 100644
--- a/src/ViewStyle.h
+++ b/src/ViewStyle.h
@@ -108,7 +108,9 @@ public:
int rightMarginWidth; ///< Spacing margin on right of text
int maskInLine; ///< Mask for markers to be put into text because there is nowhere for them to go in margin
MarginStyle ms[SC_MAX_MARGIN+1];
- int fixedColumnWidth;
+ int fixedColumnWidth; ///< Total width of margins
+ bool marginInside; ///< true: margin included in text view, false: separate views
+ int textStart; ///< Starting x position of text within the view
int zoomLevel;
WhiteSpaceVisibility viewWhitespace;
int whitespaceSize;