cocoa: started reimplementing ListView. Remove the dependancy on LCL code. Move the direct storage from NSTableView to the callback object

git-svn-id: trunk@58667 -
This commit is contained in:
dmitry 2018-08-04 04:53:02 +00:00
parent 23c3367033
commit 39cd9e7b38
2 changed files with 129 additions and 62 deletions

View File

@ -32,7 +32,7 @@ uses
cocoa_extra, CocoaPrivate,
// LCL
LMessages, LCLMessageGlue, ExtCtrls,
LCLType, LCLProc, Controls, ComCtrls, StdCtrls;
LCLType, LCLProc, Controls, StdCtrls;
type
@ -46,6 +46,10 @@ type
IListViewCallBack = interface(ICommonCallback)
procedure delayedSelectionDidChange_OnTimer(ASender: TObject);
function ItemsCount: Integer;
function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean;
procedure tableSelectionChange(ARow: Integer);
end;
TCocoaListBox = objcclass;
@ -143,12 +147,12 @@ type
TCocoaTableListView = objcclass(NSTableView, NSTableViewDelegateProtocol, NSTableViewDataSourceProtocol)
public
ListView: TCustomListView; // just reference, don't release
callback: IListViewCallback;
// Owned Pascal classes which need to be released
Items: TStringList; // Object are TStringList for sub-items
//Items: TStringList; // Object are TStringList for sub-items
Timer: TTimer;
readOnly: Boolean;
function acceptsFirstResponder: Boolean; override;
function becomeFirstResponder: Boolean; override;
@ -157,9 +161,9 @@ type
procedure lclClearCallback; override;
// Own methods, mostly convenience methods
procedure setStringValue_forCol_row(AStr: NSString; col, row: NSInteger); message 'setStringValue:forCol:row:';
procedure deleteItemForRow(row: NSInteger); message 'deleteItemForRow:';
procedure setListViewStringValue_forCol_row(AStr: NSString; col, row: NSInteger); message 'setListViewStringValue:forCol:row:';
//procedure setStringValue_forCol_row(AStr: NSString; col, row: NSInteger); message 'setStringValue:forCol:row:';
//procedure deleteItemForRow(row: NSInteger); message 'deleteItemForRow:';
//procedure setListViewStringValue_forCol_row(AStr: NSString; col, row: NSInteger); message 'setListViewStringValue:forCol:row:';
function getIndexOfColumn(ACol: NSTableColumn): NSInteger; message 'getIndexOfColumn:';
procedure reloadDataForRow_column(ARow, ACol: NSInteger); message 'reloadDataForRow:column:';
procedure scheduleSelectionDidChange(); message 'scheduleSelectionDidChange';
@ -220,9 +224,9 @@ type
procedure tableViewSelectionIsChanging(notification: NSNotification); message 'tableViewSelectionIsChanging:';}
end;
// todo: this NSScrollView should go away. TCocoaScrollView must be used instead
TCocoaListView = objcclass(NSScrollView)
public
ListView: TCustomListView; // just reference, don't release
callback: ICommonCallback;
// For report style:
TableListView: TCocoaTableListView;
@ -592,7 +596,7 @@ end;
procedure TCocoaTableListView.dealloc;
begin
if Assigned(Items) then FreeAndNil(Items);
//if Assigned(Items) then FreeAndNil(Items);
inherited dealloc;
end;
@ -601,7 +605,7 @@ begin
if not callback.resetCursorRects then
inherited resetCursorRects;
end;
(*
procedure TCocoaTableListView.setStringValue_forCol_row(
AStr: NSString; col, row: NSInteger);
var
@ -681,7 +685,7 @@ begin
lSubItems.Strings[col-1] := lNewValue;
end;
end;
*)
function TCocoaTableListView.getIndexOfColumn(ACol: NSTableColumn): NSInteger;
begin
Result := tableColumns.indexOfObject(ACol);
@ -770,10 +774,11 @@ end;
function TCocoaTableListView.numberOfRowsInTableView(tableView: NSTableView
): NSInteger;
begin
if Assigned(Items) then
Result := Items.Count
if Assigned(callback) then
Result := callback.ItemsCount
else
Result := 0;
writeln('items.Count = ',Result);
end;
function TCocoaTableListView.tableView_objectValueForTableColumn_row(
@ -782,12 +787,23 @@ var
lStringList: TStringList;
col: NSInteger;
StrResult: NSString;
txt : string;
begin
col := tableColumns.indexOfObject(tableColumn);
{$IFDEF COCOA_DEBUG_TABCONTROL}
WriteLn(Format('[TCocoaTableListView.tableView_objectValueForTableColumn_row] col=%d row=%d Items.Count=%d',
[col, row, Items.Count]));
{$ENDIF}
Result := nil;
if not Assigned(callback) then Exit;
col := tableColumns.indexOfObject(tableColumn);
txt := '';
if callback.GetItemTextAt(row, col, txt) then begin
if txt = '' then Result := NSString.string_
else Result := NSString.stringWithUTF8String(@txt[1])
end;
(*
if row > Items.Count-1 then begin
Result := nil;
Exit;
@ -800,6 +816,7 @@ begin
StrResult := NSStringUTF8(lStringList.Strings[col-1]);
end;
Result := StrResult;
*)
end;
procedure TCocoaTableListView.tableView_setObjectValue_forTableColumn_row(
@ -810,58 +827,33 @@ var
lNewValue: NSString;
begin
//WriteLn('[TCocoaTableListView.tableView_setObjectValue_forTableColumn_row]');
lNewValue := NSString(object_);
if not NSObject(object_).isKindOfClass(NSString) then Exit;
//WriteLn('[TCocoaTableListView.tableView_setObjectValue_forTableColumn_row] A');
if ListView.ReadOnly then Exit;
lNewValue := NSString(object_);
//WriteLn('[TCocoaTableListView.tableView_setObjectValue_forTableColumn_row] A');}
if ReadOnly then Exit;
lColumnIndex := getIndexOfColumn(tableColumn);
{
setListViewStringValue_forCol_row(lNewValue, lColumnIndex, row);
setStringValue_forCol_row(lNewValue, lColumnIndex, row);
//setStringValue_forCol_row(lNewValue, lColumnIndex, row);
}
reloadDataForRow_column(lColumnIndex, row);
end;
function TCocoaTableListView.tableView_shouldEditTableColumn_row(tableView: NSTableView; tableColumn: NSTableColumn; row: NSInteger): Boolean;
begin
Result := not ListView.ReadOnly;
Result := not readOnly;
end;
procedure TCocoaTableListView.tableViewSelectionDidChange(notification: NSNotification);
var
Msg: TLMNotify;
NMLV: TNMListView;
OldSel, NewSel: Integer;
NewSel: Integer;
begin
NewSel := Self.selectedRow();
{$IFDEF COCOA_DEBUG_LISTVIEW}
WriteLn(Format('[TLCLListViewCallback.SelectionChanged] NewSel=%d', [NewSel]));
{$ENDIF}
FillChar(Msg{%H-}, SizeOf(Msg), #0);
FillChar(NMLV{%H-}, SizeOf(NMLV), #0);
Msg.Msg := CN_NOTIFY;
NMLV.hdr.hwndfrom := ListView.Handle;
NMLV.hdr.code := LVN_ITEMCHANGED;
NMLV.iSubItem := 0;
NMLV.uChanged := LVIF_STATE;
Msg.NMHdr := @NMLV.hdr;
if NewSel >= 0 then
if Assigned(callback) then
begin
NMLV.iItem := NewSel;
NMLV.uNewState := LVIS_SELECTED;
end
else
begin
NMLV.iItem := 0;
NMLV.uNewState := 0;
NMLV.uOldState := LVIS_SELECTED;
NewSel := Self.selectedRow();
callback.tableSelectionChange(NewSel);
end;
LCLMessageGlue.DeliverMessage(ListView, Msg);
end;
{ TCocoaStringList }

View File

@ -221,7 +221,13 @@ type
TLCLListViewCallback = class(TLCLCommonCallback, IListViewCallback)
public
listView: TCustomListView;
tempItemsCountDelta : Integer;
procedure delayedSelectionDidChange_OnTimer(ASender: TObject);
function ItemsCount: Integer;
function GetItemTextAt(ARow, ACol: Integer; var Text: String): Boolean;
procedure tableSelectionChange(NewSel: Integer);
end;
TLCLListViewCallBackClass = class of TLCLListViewCallback;
@ -684,6 +690,7 @@ var
lCocoaLV: TCocoaListView;
lTableLV: TCocoaTableListView;
ns: NSRect;
lclcb: TLCLListViewCallback;
begin
{$IFDEF COCOA_DEBUG_LISTVIEW}
WriteLn('[TCocoaWSCustomListView.CreateHandle] AWinControl='+IntToStr(PtrInt(AWinControl)));
@ -706,13 +713,12 @@ begin
// 2-> To get proper scrolling use NSScrollView.setDocumentView instead of addSubview
// Source: http://stackoverflow.com/questions/13872642/nstableview-scrolling-does-not-work
lCocoaLV.TableListView := lTableLV;
lCocoaLV.ListView := TCustomListView(AWinControl);
lCocoaLV.setDocumentView(lTableLV);
lCocoaLV.setHasVerticalScroller(True);
lTableLV.callback := TLCLListViewCallback.Create(lTableLV, AWinControl);
lTableLV.Items := TStringList.Create;
lTableLV.ListView := TCustomListView(AWinControl);
lclcb := TLCLListViewCallback.Create(lTableLV, AWinControl);
lclcb.listView := TCustomListView(AWinControl);
lTableLV.callback := lclcb;
lTableLV.setDataSource(lTableLV);
lTableLV.setDelegate(lTableLV);
lTableLV.setAllowsColumnReordering(False);
@ -899,14 +905,24 @@ class procedure TCocoaWSCustomListView.ItemDelete(const ALV: TCustomListView;
var
lCocoaLV: TCocoaListView;
lTableLV: TCocoaTableListView;
lclcb : TLCLListViewCallback;
lStr: NSString;
begin
{$IFDEF COCOA_DEBUG_TABCONTROL}
WriteLn(Format('[TCocoaWSCustomListView.ItemDelete] AIndex=%d', [AIndex]));
{$ENDIF}
if not CheckParams(lCocoaLV, lTableLV, ALV) then Exit;
lTableLV.deleteItemForRow(AIndex);
//lTableLV.deleteItemForRow(AIndex);
lclcb := TLCLListViewCallback(lTableLV.callback.GetCallbackObject);
// TListView item would actually be removed after call to ItemDelete()
// thus have to decrease the count, as reloadDate might
// request the updated itemCount immediately
lclcb.tempItemsCountDelta := -1;
lTableLV.reloadData();
lclcb.tempItemsCountDelta := 0;
lTableLV.scheduleSelectionDidChange();
end;
@ -978,7 +994,7 @@ begin
{$IFDEF COCOA_DEBUG_TABCONTROL}
WriteLn(Format('[TCocoaWSCustomListView.ItemInsert]=> lColumnCount=%d', [lColumnCount]));
{$ENDIF}
for i := 0 to lColumnCount-1 do
{for i := 0 to lColumnCount-1 do
begin
lColumn := lTableLV.tableColumns.objectAtIndex(i);
if i = 0 then
@ -990,7 +1006,7 @@ begin
lNSStr := NSStringUTF8(lStr);
lTableLV.setStringValue_forCol_row(lNSStr, i, AIndex);
lNSStr.release;
end;
end;}
lTableLV.reloadData();
lTableLV.sizeToFit();
end;
@ -1004,9 +1020,11 @@ var
lStr: NSString;
begin
if not CheckParams(lCocoaLV, lTableLV, ALV) then Exit;
lStr := NSStringUTF8(AText);
lTableLV.setStringValue_forCol_row(lStr, ASubIndex, AItem.Index);
lStr.release;
// todo: make a specific row/column reload data!
lTableLV.reloadData();
//lStr := NSStringUTF8(AText);
//lTableLV.setStringValue_forCol_row(lStr, ASubIndex, AItem.Index);
//lStr.release;
end;
class procedure TCocoaWSCustomListView.ItemShow(const ALV: TCustomListView;
@ -1136,9 +1154,9 @@ begin
lvpHideSelection,
lvpHotTrack,}
lvpMultiSelect: lTableLV.setAllowsMultipleSelection(AIsSet);
{lvpOwnerDraw,
//lvpReadOnly, implemented elsewhere, no need for this here
lvpRowSelect,
{lvpOwnerDraw,}
lvpReadOnly: lTableLv.readOnly := AIsSet;
{ lvpRowSelect,
lvpShowColumnHeaders,
lvpShowWorkAreas,
lvpWrapText,
@ -1255,6 +1273,7 @@ end; *)
{ TLCLListViewCallback }
procedure TLCLListViewCallback.delayedSelectionDidChange_OnTimer(
ASender: TObject);
begin
@ -1262,6 +1281,62 @@ begin
TCocoaTableListView(Owner).tableViewSelectionDidChange(nil);
end;
function TLCLListViewCallback.ItemsCount: Integer;
begin
Result:=listView.Items.Count + tempItemsCountDelta;
end;
function TLCLListViewCallback.GetItemTextAt(ARow, ACol: Integer;
var Text: String): Boolean;
begin
Result := (ACol>=0) and (ACol<listView.ColumnCount)
and (ARow >= 0) and (ARow < listView.Items.Count);
if not Result then Exit;
if ACol = 0 then
Text := listView.Items[ARow].Caption
else
begin
Text := '';
if (ACol >=0) and (ACol < listView.Items[ARow].SubItems.Count) then
Text := listView.Items[ARow].SubItems[ACol];
end;
end;
procedure TLCLListViewCallback.tableSelectionChange(NewSel: Integer);
var
Msg: TLMNotify;
NMLV: TNMListView;
begin
{$IFDEF COCOA_DEBUG_LISTVIEW}
WriteLn(Format('[TLCLListViewCallback.SelectionChanged] NewSel=%d', [NewSel]));
{$ENDIF}
FillChar(Msg{%H-}, SizeOf(Msg), #0);
FillChar(NMLV{%H-}, SizeOf(NMLV), #0);
Msg.Msg := CN_NOTIFY;
NMLV.hdr.hwndfrom := ListView.Handle;
NMLV.hdr.code := LVN_ITEMCHANGED;
NMLV.iSubItem := 0;
NMLV.uChanged := LVIF_STATE;
Msg.NMHdr := @NMLV.hdr;
if NewSel >= 0 then
begin
NMLV.iItem := NewSel;
NMLV.uNewState := LVIS_SELECTED;
end
else
begin
NMLV.iItem := 0;
NMLV.uNewState := 0;
NMLV.uOldState := LVIS_SELECTED;
end;
LCLMessageGlue.DeliverMessage(ListView, Msg);
end;
{ TCocoaWSTrackBar }
{------------------------------------------------------------------------------