Implement vertical alignment in wxDataViewCtrl in wxOSX

When using non-default row height, text would be aligned to the top of
the row. This is a known limitation of NSTextFieldCell and the solution
is to either switch to modern view-based rendering or customize the cell
as this commit does.

See https://red-sweater.com/blog/148/what-a-difference-a-cell-makes for
Daniel Jalkut's description of this solution.
This commit is contained in:
Václav Slavík 2016-11-21 18:44:11 +01:00
parent 818b084b4b
commit 92dc929b3f
2 changed files with 98 additions and 6 deletions

View File

@ -367,6 +367,16 @@ private:
-(NSSize) cellSize;
@end
// ============================================================================
// NSTextFieldCell customized to allow vertical alignment
// ============================================================================
@interface wxTextFieldCell : NSTextFieldCell
-(void) setWXAlignment:(int)alignment;
@end
// ============================================================================
// wxImageTextCell
// ============================================================================
@ -382,7 +392,7 @@ private:
// into their reserved space. Smaller or not existing images use the fixed
// reserved size and are scaled if necessary.
//
@interface wxImageTextCell : NSTextFieldCell
@interface wxImageTextCell : wxTextFieldCell
{
@private
CGFloat xImageShift; // shift for the image in x-direction from border

View File

@ -40,6 +40,9 @@
#define DataViewPboardType @"OutlineViewItem"
static const int MINIMUM_NATIVE_ROW_HEIGHT = 17;
// ============================================================================
// Classes used locally in dataview.mm
// ============================================================================
@ -1231,7 +1234,7 @@ outlineView:(NSOutlineView*)outlineView
@end
// ============================================================================
// wxImageTextCell
// wxImageCell
// ============================================================================
@implementation wxImageCell
@ -1245,6 +1248,84 @@ outlineView:(NSOutlineView*)outlineView
@end
// ============================================================================
// wxTextFieldCell
// ============================================================================
@interface wxTextFieldCell ()
{
int _wxAlignment;
BOOL _adjustRect;
}
@end
@implementation wxTextFieldCell
- (void)setWXAlignment:(int)alignment
{
_wxAlignment = alignment;
_adjustRect = (alignment & (wxALIGN_CENTRE_VERTICAL | wxALIGN_BOTTOM)) != 0;
}
// These three overrides implement vertical alignment of text cells.
// The solution is described by Daniel Jalkut at
// https://red-sweater.com/blog/148/what-a-difference-a-cell-makes
- (NSRect)drawingRectForBounds:(NSRect)theRect
{
// Get the parent's idea of where we should draw
NSRect r = [super drawingRectForBounds:theRect];
if (!_adjustRect)
return r;
if (theRect.size.height <= MINIMUM_NATIVE_ROW_HEIGHT)
return r; // don't mess with default-sized rows as they are centered
NSSize bestSize = [self cellSizeForBounds:theRect];
if (bestSize.height < r.size.height)
{
if (_wxAlignment & wxALIGN_CENTER_VERTICAL)
{
r.origin.y += int(r.size.height - bestSize.height) / 2;
r.size.height = bestSize.height;
}
else if (_wxAlignment & wxALIGN_BOTTOM)
{
r.origin.y += r.size.height - bestSize.height;
r.size.height = bestSize.height;
}
}
return r;
}
- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(NSInteger)selStart length:(NSInteger)selLength
{
BOOL oldAdjustRect = _adjustRect;
if (oldAdjustRect)
{
aRect = [self drawingRectForBounds:aRect];
_adjustRect = NO;
}
[super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
_adjustRect = oldAdjustRect;
}
- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent
{
BOOL oldAdjustRect = _adjustRect;
if (oldAdjustRect)
{
aRect = [self drawingRectForBounds:aRect];
_adjustRect = NO;
}
[super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
_adjustRect = oldAdjustRect;
}
@end
// ============================================================================
// wxImageTextCell
// ============================================================================
@ -2441,7 +2522,6 @@ void wxCocoaDataViewControl::SetRowHeight(int height)
int wxCocoaDataViewControl::GetDefaultRowHeight() const
{
const int MINIMUM_NATIVE_HEIGHT = 17;
// Custom setup of NSLayoutManager is necessary to match NSTableView sizing.
// See http://stackoverflow.com/questions/17095927/dynamically-changing-row-height-after-font-size-of-entire-nstableview-nsoutlin
NSLayoutManager *lm = [[NSLayoutManager alloc] init];
@ -2449,7 +2529,7 @@ int wxCocoaDataViewControl::GetDefaultRowHeight() const
[lm setUsesScreenFonts:NO];
CGFloat height = [lm defaultLineHeightForFont:GetWXPeer()->GetFont().OSXGetNSFont()];
[lm release];
return wxMax(MINIMUM_NATIVE_HEIGHT, int(height));
return wxMax(MINIMUM_NATIVE_ROW_HEIGHT, int(height));
}
void wxCocoaDataViewControl::SetRowHeight(const wxDataViewItem& WXUNUSED(item), unsigned int WXUNUSED(height))
@ -2653,6 +2733,8 @@ void wxDataViewRenderer::OSXUpdateAlignment()
int align = GetEffectiveAlignment();
NSCell *cell = GetNativeData()->GetColumnCell();
[cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
if ([cell respondsToSelector:@selector(setWXAlignment:)])
[(wxTextFieldCell*)cell setWXAlignment:align];
}
void wxDataViewRenderer::SetMode(wxDataViewCellMode mode)
@ -2829,7 +2911,7 @@ wxDataViewTextRenderer::wxDataViewTextRenderer(const wxString& varianttype,
NSTextFieldCell* cell;
cell = [[NSTextFieldCell alloc] init];
cell = [[wxTextFieldCell alloc] init];
[cell setAlignment:ConvertToNativeHorizontalTextAlignment(align)];
SetNativeData(new wxDataViewRendererNativeData(cell));
[cell release];
@ -3050,7 +3132,7 @@ wxDataViewDateRenderer::wxDataViewDateRenderer(const wxString& varianttype,
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setDateStyle:NSDateFormatterShortStyle];
cell = [[NSTextFieldCell alloc] init];
cell = [[wxTextFieldCell alloc] init];
[cell setFormatter:dateFormatter];
SetNativeData(new wxDataViewRendererNativeData(cell,[NSDate dateWithString:@"2000-12-30 20:00:00 +0000"]));
[cell release];