Implement macOS-specific wxDataViewCheckIconTextRenderer

This implementation suffers at least from 2 problems:

1. It doesn't support icons at all.
2. It toggles the checkbox when clicking on the text and not just on the
   checkbox itself, as would be expected.

but it's still better than the current version which simply doesn't work
at all, i.e. can't be toggled in any way (and also doesn't draw itself
correctly when using dark mode under macOS 10.14+), so use it for now.

A better solution would be to fix the problem with ActivateCell() not
working at all (see #17746) and update the code to respect drawing in
dark mode.

Closes #17473.

Closes https://github.com/wxWidgets/wxWidgets/pull/904
This commit is contained in:
Stefan Csomor
2019-10-21 02:04:35 +02:00
committed by Vadim Zeitlin
parent fa3c0b1808
commit 235e61c311
4 changed files with 177 additions and 34 deletions

View File

@@ -539,6 +539,8 @@ typedef wxDataViewTextRenderer wxDataViewDateRenderer;
// wxDataViewCheckIconTextRenderer: 3-state checkbox + text + optional icon // wxDataViewCheckIconTextRenderer: 3-state checkbox + text + optional icon
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#ifndef __WXOSX__
class WXDLLIMPEXP_CORE wxDataViewCheckIconTextRenderer class WXDLLIMPEXP_CORE wxDataViewCheckIconTextRenderer
: public wxDataViewCustomRenderer : public wxDataViewCustomRenderer
{ {
@@ -589,6 +591,7 @@ private:
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewCheckIconTextRenderer); wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewCheckIconTextRenderer);
}; };
#endif // !__WXOSX__
// this class is obsolete, its functionality was merged in // this class is obsolete, its functionality was merged in
// wxDataViewTextRenderer itself now, don't use it any more // wxDataViewTextRenderer itself now, don't use it any more

View File

@@ -184,6 +184,40 @@ private:
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewIconTextRenderer); wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewIconTextRenderer);
}; };
// ---------------------------------------------------------
// wxDataViewIconTextRenderer
// ---------------------------------------------------------
class WXDLLIMPEXP_CORE wxDataViewCheckIconTextRenderer
: public wxDataViewRenderer
{
public:
static wxString GetDefaultType() { return wxS("wxDataViewCheckIconText"); }
explicit wxDataViewCheckIconTextRenderer
(
wxDataViewCellMode mode = wxDATAVIEW_CELL_ACTIVATABLE,
int align = wxDVR_DEFAULT_ALIGNMENT
);
// This renderer can always display the 3rd ("indeterminate") checkbox
// state if the model contains cells with wxCHK_UNDETERMINED value, but it
// doesn't allow the user to set it by default. Call this method to allow
// this to happen.
void Allow3rdStateForUser(bool allow = true);
virtual bool MacRender() wxOVERRIDE;
virtual void OSXOnCellChanged(NSObject *value,
const wxDataViewItem& item,
unsigned col) wxOVERRIDE;
private:
bool m_allow3rdStateForUser;
wxDECLARE_DYNAMIC_CLASS_NO_COPY(wxDataViewCheckIconTextRenderer);
};
// --------------------------------------------------------- // ---------------------------------------------------------
// wxDataViewToggleRenderer // wxDataViewToggleRenderer
// --------------------------------------------------------- // ---------------------------------------------------------

View File

@@ -1972,6 +1972,8 @@ wxSize wxDataViewDateRenderer::GetSize() const
// wxDataViewCheckIconTextRenderer implementation // wxDataViewCheckIconTextRenderer implementation
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
#ifndef __WXOSX__
IMPLEMENT_VARIANT_OBJECT_EXPORTED(wxDataViewCheckIconText, WXDLLIMPEXP_ADV) IMPLEMENT_VARIANT_OBJECT_EXPORTED(wxDataViewCheckIconText, WXDLLIMPEXP_ADV)
wxIMPLEMENT_CLASS(wxDataViewCheckIconText, wxDataViewIconText); wxIMPLEMENT_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
@@ -2162,6 +2164,8 @@ wxSize wxDataViewCheckIconTextRenderer::GetCheckSize() const
return wxRendererNative::Get().GetCheckBoxSize(GetView()); return wxRendererNative::Get().GetCheckBoxSize(GetView());
} }
#endif // !__WXOSX__
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// wxDataViewListStore // wxDataViewListStore
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@@ -540,7 +540,7 @@ outlineView:(NSOutlineView*)outlineView
item:(id)item childIndex:(NSInteger)index item:(id)item childIndex:(NSInteger)index
{ {
wxUnusedVar(outlineView); wxUnusedVar(outlineView);
return [self setupAndCallDataViewEvents:wxEVT_DATAVIEW_ITEM_DROP dropInfo:info item:item proposedChildIndex:index] != NSDragOperationNone; return [self setupAndCallDataViewEvents:wxEVT_DATAVIEW_ITEM_DROP dropInfo:info item:item proposedChildIndex:index] != NSDragOperationNone;
} }
@@ -655,7 +655,7 @@ outlineView:(NSOutlineView*)outlineView
-(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors -(void) outlineView:(NSOutlineView*)outlineView sortDescriptorsDidChange:(NSArray*)oldDescriptors
{ {
wxUnusedVar(oldDescriptors); wxUnusedVar(oldDescriptors);
// Warning: the new sort descriptors are guaranteed to be only of type // Warning: the new sort descriptors are guaranteed to be only of type
// NSSortDescriptor! Therefore, the sort descriptors for the data source // NSSortDescriptor! Therefore, the sort descriptors for the data source
// have to be converted. // have to be converted.
@@ -734,18 +734,18 @@ outlineView:(NSOutlineView*)outlineView
} }
wxDataFormatId formatId = event.GetDataFormat().GetType(); wxDataFormatId formatId = event.GetDataFormat().GetType();
wxMemoryBuffer buffer; wxMemoryBuffer buffer;
// copy data into buffer: // copy data into buffer:
if ( formatId != wxDF_INVALID) if ( formatId != wxDF_INVALID)
{ {
size_t size = dataObjects->GetDataSize(formatId); size_t size = dataObjects->GetDataSize(formatId);
event.SetDataSize(size); event.SetDataSize(size);
dataObjects->GetDataHere(formatId,buffer.GetWriteBuf(size)); dataObjects->GetDataHere(formatId,buffer.GetWriteBuf(size));
buffer.UngetWriteBuf(size); buffer.UngetWriteBuf(size);
event.SetDataBuffer(buffer.GetData()); event.SetDataBuffer(buffer.GetData());
} }
// finally, send event: // finally, send event:
if (dvc->HandleWindowEvent(event) && event.IsAllowed()) if (dvc->HandleWindowEvent(event) && event.IsAllowed())
{ {
@@ -788,38 +788,38 @@ outlineView:(NSOutlineView*)outlineView
NSArray* supportedTypes( NSArray* supportedTypes(
[NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil] [NSArray arrayWithObjects:DataViewPboardType,NSStringPboardType,nil]
); );
NSPasteboard* pasteboard([info draggingPasteboard]); NSPasteboard* pasteboard([info draggingPasteboard]);
NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]); NSString* bestType([pasteboard availableTypeFromArray:supportedTypes]);
if ( bestType == nil ) if ( bestType == nil )
return NSDragOperationNone; return NSDragOperationNone;
NSDragOperation dragOperation = NSDragOperationNone; NSDragOperation dragOperation = NSDragOperationNone;
wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl()); wxDataViewCtrl* const dvc(implementation->GetDataViewCtrl());
wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly."); wxCHECK_MSG(dvc, false, "Pointer to data view control not set correctly.");
wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly."); wxCHECK_MSG(dvc->GetModel(), false, "Pointer to model not set correctly.");
// wxDataViewEvent event(eventType, dvc, wxDataViewItemFromItem(item)); // wxDataViewEvent event(eventType, dvc, wxDataViewItemFromItem(item));
if ([bestType compare:DataViewPboardType] == NSOrderedSame) if ([bestType compare:DataViewPboardType] == NSOrderedSame)
{ {
NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]); NSArray* dataArray((NSArray*)[pasteboard propertyListForType:DataViewPboardType]);
NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]); NSUInteger indexDraggedItem, noOfDraggedItems([dataArray count]);
indexDraggedItem = 0; indexDraggedItem = 0;
while (indexDraggedItem < noOfDraggedItems) while (indexDraggedItem < noOfDraggedItems)
{ {
wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem])); wxDataObjectComposite* dataObjects(implementation->GetDnDDataObjects((NSData*)[dataArray objectAtIndex:indexDraggedItem]));
dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index]; dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index];
if ( dragOperation != NSDragOperationNone ) if ( dragOperation != NSDragOperationNone )
++indexDraggedItem; ++indexDraggedItem;
else else
indexDraggedItem = noOfDraggedItems; indexDraggedItem = noOfDraggedItems;
// clean-up: // clean-up:
delete dataObjects; delete dataObjects;
} }
@@ -831,7 +831,7 @@ outlineView:(NSOutlineView*)outlineView
CFDataRef osxData; CFDataRef osxData;
wxDataObjectComposite* dataObjects (new wxDataObjectComposite()); wxDataObjectComposite* dataObjects (new wxDataObjectComposite());
wxTextDataObject* textDataObject(new wxTextDataObject()); wxTextDataObject* textDataObject(new wxTextDataObject());
osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType], osxData = ::CFStringCreateExternalRepresentation(kCFAllocatorDefault,(CFStringRef)[pasteboard stringForType:NSStringPboardType],
#if defined(wxNEEDS_UTF16_FOR_TEXT_DATAOBJ) #if defined(wxNEEDS_UTF16_FOR_TEXT_DATAOBJ)
kCFStringEncodingUTF16, kCFStringEncodingUTF16,
@@ -844,14 +844,14 @@ outlineView:(NSOutlineView*)outlineView
else else
delete textDataObject; delete textDataObject;
// send event if data could be copied: // send event if data could be copied:
dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index]; dragOperation = [self callDataViewEvents:eventType dataObjects:dataObjects item:item proposedChildIndex:index];
// clean up: // clean up:
::CFRelease(osxData); ::CFRelease(osxData);
delete dataObjects; delete dataObjects;
} }
return dragOperation; return dragOperation;
} }
@@ -1179,16 +1179,16 @@ outlineView:(NSOutlineView*)outlineView
wxDataViewCustomRenderer * const renderer = obj->customRenderer; wxDataViewCustomRenderer * const renderer = obj->customRenderer;
// if this method is called everything is already setup correctly, // if this method is called everything is already setup correctly,
CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort]; CGContextRef context = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
CGContextSaveGState( context ); CGContextSaveGState( context );
if ( ![controlView isFlipped] ) if ( ![controlView isFlipped] )
{ {
CGContextTranslateCTM( context, 0, [controlView bounds].size.height ); CGContextTranslateCTM( context, 0, [controlView bounds].size.height );
CGContextScaleCTM( context, 1, -1 ); CGContextScaleCTM( context, 1, -1 );
} }
wxGCDC dc; wxGCDC dc;
wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context); wxGraphicsContext* gc = wxGraphicsContext::CreateFromNative(context);
dc.SetGraphicsContext(gc); dc.SetGraphicsContext(gc);
@@ -1709,7 +1709,7 @@ outlineView:(NSOutlineView*)outlineView
dvc->GetEventHandler()->ProcessEvent(event); dvc->GetEventHandler()->ProcessEvent(event);
} }
// Default enter key behaviour is to begin cell editing. Subclass keyDown to // Default enter key behaviour is to begin cell editing. Subclass keyDown to
// provide a keyboard wxEVT_DATAVIEW_ITEM_ACTIVATED event and allow the NSEvent // provide a keyboard wxEVT_DATAVIEW_ITEM_ACTIVATED event and allow the NSEvent
// to pass if the wxEvent is not processed. // to pass if the wxEvent is not processed.
- (void)keyDown:(NSEvent *)event - (void)keyDown:(NSEvent *)event
@@ -1926,19 +1926,19 @@ outlineView:(NSOutlineView*)outlineView
{ {
currentlyEditedColumn = [self editedColumn]; currentlyEditedColumn = [self editedColumn];
currentlyEditedRow = [self editedRow]; currentlyEditedRow = [self editedRow];
wxDataViewItem item = wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]); wxDataViewItem item = wxDataViewItemFromItem([self itemAtRow:currentlyEditedRow]);
NSTableColumn* tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn]; NSTableColumn* tableColumn = [[self tableColumns] objectAtIndex:currentlyEditedColumn];
wxDataViewColumn* const col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]); wxDataViewColumn* const col([static_cast<wxDVCNSTableColumn*>(tableColumn) getColumnPointer]);
wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl(); wxDataViewCtrl* const dvc = implementation->GetDataViewCtrl();
// Before doing anything we send an event asking if editing of this item is really wanted. // Before doing anything we send an event asking if editing of this item is really wanted.
wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_START_EDITING, dvc, col, item); wxDataViewEvent event(wxEVT_DATAVIEW_ITEM_START_EDITING, dvc, col, item);
dvc->GetEventHandler()->ProcessEvent( event ); dvc->GetEventHandler()->ProcessEvent( event );
if( !event.IsAllowed() ) if( !event.IsAllowed() )
return NO; return NO;
return YES; return YES;
} }
@@ -2402,7 +2402,7 @@ bool wxCocoaDataViewControl::Remove(const wxDataViewItem& parent)
bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr) bool wxCocoaDataViewControl::Update(const wxDataViewColumn *columnPtr)
{ {
wxUnusedVar(columnPtr); wxUnusedVar(columnPtr);
return false; return false;
} }
@@ -3312,28 +3312,130 @@ void wxDataViewIconTextRenderer::OSXOnCellChanged(NSObject *value,
unsigned col) unsigned col)
{ {
wxDataViewModel *model = GetOwner()->GetOwner()->GetModel(); wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
// The icon can't be edited so get its old value and reuse it. // The icon can't be edited so get its old value and reuse it.
wxVariant valueOld; wxVariant valueOld;
model->GetValue(valueOld, item, col); model->GetValue(valueOld, item, col);
wxDataViewIconText iconText; wxDataViewIconText iconText;
iconText << valueOld; iconText << valueOld;
// But replace the text with the value entered by user. // But replace the text with the value entered by user.
iconText.SetText(ObjectToString(value)); iconText.SetText(ObjectToString(value));
wxVariant valueIconText; wxVariant valueIconText;
valueIconText << iconText; valueIconText << iconText;
if ( !Validate(valueIconText) ) if ( !Validate(valueIconText) )
return; return;
model->ChangeValue(valueIconText, item, col); model->ChangeValue(valueIconText, item, col);
} }
wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer); wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewIconTextRenderer,wxDataViewRenderer);
// ---------------------------------------------------------
// wxDataViewCheckIconTextRenderer
// ---------------------------------------------------------
IMPLEMENT_VARIANT_OBJECT_EXPORTED(wxDataViewCheckIconText, WXDLLIMPEXP_CORE)
wxIMPLEMENT_CLASS(wxDataViewCheckIconText, wxDataViewIconText);
wxDataViewCheckIconTextRenderer::wxDataViewCheckIconTextRenderer
(
wxDataViewCellMode mode,
int align
)
: wxDataViewRenderer(GetDefaultType(), mode,mode)
{
m_allow3rdStateForUser = false;
NSButtonCell* cell;
cell = [[NSButtonCell alloc] init];
[cell setAlignment:ConvertToNativeHorizontalTextAlignment(GetAlignment())];
[cell setButtonType: NSSwitchButton];
[cell setImagePosition:NSImageLeft];
[cell setAllowsMixedState:YES];
SetNativeData(new wxDataViewRendererNativeData(cell));
[cell release];
}
void wxDataViewCheckIconTextRenderer::Allow3rdStateForUser(bool allow)
{
m_allow3rdStateForUser = allow;
}
bool wxDataViewCheckIconTextRenderer::MacRender()
{
wxDataViewCheckIconText checkIconText;
checkIconText << GetValue();
NSButtonCell* cell = (NSButtonCell*) GetNativeData()->GetItemCell();
int nativecbvalue = 0;
switch ( checkIconText.GetCheckedState() )
{
case wxCHK_CHECKED:
nativecbvalue = 1;
break;
case wxCHK_UNDETERMINED:
nativecbvalue = -1;
break;
case wxCHK_UNCHECKED:
nativecbvalue = 0;
break;
}
[cell setIntValue:nativecbvalue];
[cell setTitle:wxCFStringRef(checkIconText.GetText()).AsNSString()];
return true;
}
void wxDataViewCheckIconTextRenderer::OSXOnCellChanged(NSObject *value,
const wxDataViewItem& item,
unsigned col)
{
wxDataViewModel *model = GetOwner()->GetOwner()->GetModel();
// The icon can't be edited so get its old value and reuse it.
wxVariant valueOld;
model->GetValue(valueOld, item, col);
wxDataViewCheckIconText checkIconText;
checkIconText << valueOld;
wxCheckBoxState checkedState ;
switch ( ObjectToLong(value) )
{
case 1:
checkedState = wxCHK_CHECKED;
break;
case 0:
checkedState = wxCHK_UNCHECKED;
break;
case -1:
checkedState = m_allow3rdStateForUser ? wxCHK_UNDETERMINED : wxCHK_CHECKED;
break;
}
checkIconText.SetCheckedState(checkedState);
wxVariant valueIconText;
valueIconText << checkIconText;
if ( !Validate(valueIconText) )
return;
model->ChangeValue(valueIconText, item, col);
}
wxIMPLEMENT_ABSTRACT_CLASS(wxDataViewCheckIconTextRenderer,wxDataViewRenderer);
// --------------------------------------------------------- // ---------------------------------------------------------
// wxDataViewToggleRenderer // wxDataViewToggleRenderer
// --------------------------------------------------------- // ---------------------------------------------------------
@@ -3398,7 +3500,7 @@ wxDataViewProgressRenderer::wxDataViewProgressRenderer(const wxString& label,
: wxDataViewRenderer(varianttype,mode,align) : wxDataViewRenderer(varianttype,mode,align)
{ {
wxUnusedVar(label); wxUnusedVar(label);
NSLevelIndicatorCell* cell; NSLevelIndicatorCell* cell;
cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]; cell = [[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];