Browse Source

* Table demos

michael 4 years ago
parent
commit
6597c81be0

+ 246 - 0
demo/webwidget/widgets/countrycodes.js

@@ -0,0 +1,246 @@
+var countrycodes = 
+[ 
+  {name: 'Afghanistan', code: 'AF'}, 
+  {name: 'Åland Islands', code: 'AX'}, 
+  {name: 'Albania', code: 'AL'}, 
+  {name: 'Algeria', code: 'DZ'}, 
+  {name: 'American Samoa', code: 'AS'}, 
+  {name: 'AndorrA', code: 'AD'}, 
+  {name: 'Angola', code: 'AO'}, 
+  {name: 'Anguilla', code: 'AI'}, 
+  {name: 'Antarctica', code: 'AQ'}, 
+  {name: 'Antigua and Barbuda', code: 'AG'}, 
+  {name: 'Argentina', code: 'AR'}, 
+  {name: 'Armenia', code: 'AM'}, 
+  {name: 'Aruba', code: 'AW'}, 
+  {name: 'Australia', code: 'AU'}, 
+  {name: 'Austria', code: 'AT'}, 
+  {name: 'Azerbaijan', code: 'AZ'}, 
+  {name: 'Bahamas', code: 'BS'}, 
+  {name: 'Bahrain', code: 'BH'}, 
+  {name: 'Bangladesh', code: 'BD'}, 
+  {name: 'Barbados', code: 'BB'}, 
+  {name: 'Belarus', code: 'BY'}, 
+  {name: 'Belgium', code: 'BE'}, 
+  {name: 'Belize', code: 'BZ'}, 
+  {name: 'Benin', code: 'BJ'}, 
+  {name: 'Bermuda', code: 'BM'}, 
+  {name: 'Bhutan', code: 'BT'}, 
+  {name: 'Bolivia', code: 'BO'}, 
+  {name: 'Bosnia and Herzegovina', code: 'BA'}, 
+  {name: 'Botswana', code: 'BW'}, 
+  {name: 'Bouvet Island', code: 'BV'}, 
+  {name: 'Brazil', code: 'BR'}, 
+  {name: 'British Indian Ocean Territory', code: 'IO'}, 
+  {name: 'Brunei Darussalam', code: 'BN'}, 
+  {name: 'Bulgaria', code: 'BG'}, 
+  {name: 'Burkina Faso', code: 'BF'}, 
+  {name: 'Burundi', code: 'BI'}, 
+  {name: 'Cambodia', code: 'KH'}, 
+  {name: 'Cameroon', code: 'CM'}, 
+  {name: 'Canada', code: 'CA'}, 
+  {name: 'Cape Verde', code: 'CV'}, 
+  {name: 'Cayman Islands', code: 'KY'}, 
+  {name: 'Central African Republic', code: 'CF'}, 
+  {name: 'Chad', code: 'TD'}, 
+  {name: 'Chile', code: 'CL'}, 
+  {name: 'China', code: 'CN'}, 
+  {name: 'Christmas Island', code: 'CX'}, 
+  {name: 'Cocos (Keeling) Islands', code: 'CC'}, 
+  {name: 'Colombia', code: 'CO'}, 
+  {name: 'Comoros', code: 'KM'}, 
+  {name: 'Congo', code: 'CG'}, 
+  {name: 'Congo, The Democratic Republic of the', code: 'CD'}, 
+  {name: 'Cook Islands', code: 'CK'}, 
+  {name: 'Costa Rica', code: 'CR'}, 
+  {name: 'Cote D\'Ivoire', code: 'CI'}, 
+  {name: 'Croatia', code: 'HR'}, 
+  {name: 'Cuba', code: 'CU'}, 
+  {name: 'Cyprus', code: 'CY'}, 
+  {name: 'Czech Republic', code: 'CZ'}, 
+  {name: 'Denmark', code: 'DK'}, 
+  {name: 'Djibouti', code: 'DJ'}, 
+  {name: 'Dominica', code: 'DM'}, 
+  {name: 'Dominican Republic', code: 'DO'}, 
+  {name: 'Ecuador', code: 'EC'}, 
+  {name: 'Egypt', code: 'EG'}, 
+  {name: 'El Salvador', code: 'SV'}, 
+  {name: 'Equatorial Guinea', code: 'GQ'}, 
+  {name: 'Eritrea', code: 'ER'}, 
+  {name: 'Estonia', code: 'EE'}, 
+  {name: 'Ethiopia', code: 'ET'}, 
+  {name: 'Falkland Islands (Malvinas)', code: 'FK'}, 
+  {name: 'Faroe Islands', code: 'FO'}, 
+  {name: 'Fiji', code: 'FJ'}, 
+  {name: 'Finland', code: 'FI'}, 
+  {name: 'France', code: 'FR'}, 
+  {name: 'French Guiana', code: 'GF'}, 
+  {name: 'French Polynesia', code: 'PF'}, 
+  {name: 'French Southern Territories', code: 'TF'}, 
+  {name: 'Gabon', code: 'GA'}, 
+  {name: 'Gambia', code: 'GM'}, 
+  {name: 'Georgia', code: 'GE'}, 
+  {name: 'Germany', code: 'DE'}, 
+  {name: 'Ghana', code: 'GH'}, 
+  {name: 'Gibraltar', code: 'GI'}, 
+  {name: 'Greece', code: 'GR'}, 
+  {name: 'Greenland', code: 'GL'}, 
+  {name: 'Grenada', code: 'GD'}, 
+  {name: 'Guadeloupe', code: 'GP'}, 
+  {name: 'Guam', code: 'GU'}, 
+  {name: 'Guatemala', code: 'GT'}, 
+  {name: 'Guernsey', code: 'GG'}, 
+  {name: 'Guinea', code: 'GN'}, 
+  {name: 'Guinea-Bissau', code: 'GW'}, 
+  {name: 'Guyana', code: 'GY'}, 
+  {name: 'Haiti', code: 'HT'}, 
+  {name: 'Heard Island and Mcdonald Islands', code: 'HM'}, 
+  {name: 'Holy See (Vatican City State)', code: 'VA'}, 
+  {name: 'Honduras', code: 'HN'}, 
+  {name: 'Hong Kong', code: 'HK'}, 
+  {name: 'Hungary', code: 'HU'}, 
+  {name: 'Iceland', code: 'IS'}, 
+  {name: 'India', code: 'IN'}, 
+  {name: 'Indonesia', code: 'ID'}, 
+  {name: 'Iran, Islamic Republic Of', code: 'IR'}, 
+  {name: 'Iraq', code: 'IQ'}, 
+  {name: 'Ireland', code: 'IE'}, 
+  {name: 'Isle of Man', code: 'IM'}, 
+  {name: 'Israel', code: 'IL'}, 
+  {name: 'Italy', code: 'IT'}, 
+  {name: 'Jamaica', code: 'JM'}, 
+  {name: 'Japan', code: 'JP'}, 
+  {name: 'Jersey', code: 'JE'}, 
+  {name: 'Jordan', code: 'JO'}, 
+  {name: 'Kazakhstan', code: 'KZ'}, 
+  {name: 'Kenya', code: 'KE'}, 
+  {name: 'Kiribati', code: 'KI'}, 
+  {name: 'Korea, Democratic People\'S Republic of', code: 'KP'}, 
+  {name: 'Korea, Republic of', code: 'KR'}, 
+  {name: 'Kuwait', code: 'KW'}, 
+  {name: 'Kyrgyzstan', code: 'KG'}, 
+  {name: 'Lao People\'S Democratic Republic', code: 'LA'}, 
+  {name: 'Latvia', code: 'LV'}, 
+  {name: 'Lebanon', code: 'LB'}, 
+  {name: 'Lesotho', code: 'LS'}, 
+  {name: 'Liberia', code: 'LR'}, 
+  {name: 'Libyan Arab Jamahiriya', code: 'LY'}, 
+  {name: 'Liechtenstein', code: 'LI'}, 
+  {name: 'Lithuania', code: 'LT'}, 
+  {name: 'Luxembourg', code: 'LU'}, 
+  {name: 'Macao', code: 'MO'}, 
+  {name: 'Macedonia, The Former Yugoslav Republic of', code: 'MK'}, 
+  {name: 'Madagascar', code: 'MG'}, 
+  {name: 'Malawi', code: 'MW'}, 
+  {name: 'Malaysia', code: 'MY'}, 
+  {name: 'Maldives', code: 'MV'}, 
+  {name: 'Mali', code: 'ML'}, 
+  {name: 'Malta', code: 'MT'}, 
+  {name: 'Marshall Islands', code: 'MH'}, 
+  {name: 'Martinique', code: 'MQ'}, 
+  {name: 'Mauritania', code: 'MR'}, 
+  {name: 'Mauritius', code: 'MU'}, 
+  {name: 'Mayotte', code: 'YT'}, 
+  {name: 'Mexico', code: 'MX'}, 
+  {name: 'Micronesia, Federated States of', code: 'FM'}, 
+  {name: 'Moldova, Republic of', code: 'MD'}, 
+  {name: 'Monaco', code: 'MC'}, 
+  {name: 'Mongolia', code: 'MN'}, 
+  {name: 'Montserrat', code: 'MS'}, 
+  {name: 'Morocco', code: 'MA'}, 
+  {name: 'Mozambique', code: 'MZ'}, 
+  {name: 'Myanmar', code: 'MM'}, 
+  {name: 'Namibia', code: 'NA'}, 
+  {name: 'Nauru', code: 'NR'}, 
+  {name: 'Nepal', code: 'NP'}, 
+  {name: 'Netherlands', code: 'NL'}, 
+  {name: 'Netherlands Antilles', code: 'AN'}, 
+  {name: 'New Caledonia', code: 'NC'}, 
+  {name: 'New Zealand', code: 'NZ'}, 
+  {name: 'Nicaragua', code: 'NI'}, 
+  {name: 'Niger', code: 'NE'}, 
+  {name: 'Nigeria', code: 'NG'}, 
+  {name: 'Niue', code: 'NU'}, 
+  {name: 'Norfolk Island', code: 'NF'}, 
+  {name: 'Northern Mariana Islands', code: 'MP'}, 
+  {name: 'Norway', code: 'NO'}, 
+  {name: 'Oman', code: 'OM'}, 
+  {name: 'Pakistan', code: 'PK'}, 
+  {name: 'Palau', code: 'PW'}, 
+  {name: 'Palestinian Territory, Occupied', code: 'PS'}, 
+  {name: 'Panama', code: 'PA'}, 
+  {name: 'Papua New Guinea', code: 'PG'}, 
+  {name: 'Paraguay', code: 'PY'}, 
+  {name: 'Peru', code: 'PE'}, 
+  {name: 'Philippines', code: 'PH'}, 
+  {name: 'Pitcairn', code: 'PN'}, 
+  {name: 'Poland', code: 'PL'}, 
+  {name: 'Portugal', code: 'PT'}, 
+  {name: 'Puerto Rico', code: 'PR'}, 
+  {name: 'Qatar', code: 'QA'}, 
+  {name: 'Reunion', code: 'RE'}, 
+  {name: 'Romania', code: 'RO'}, 
+  {name: 'Russian Federation', code: 'RU'}, 
+  {name: 'RWANDA', code: 'RW'}, 
+  {name: 'Saint Helena', code: 'SH'}, 
+  {name: 'Saint Kitts and Nevis', code: 'KN'}, 
+  {name: 'Saint Lucia', code: 'LC'}, 
+  {name: 'Saint Pierre and Miquelon', code: 'PM'}, 
+  {name: 'Saint Vincent and the Grenadines', code: 'VC'}, 
+  {name: 'Samoa', code: 'WS'}, 
+  {name: 'San Marino', code: 'SM'}, 
+  {name: 'Sao Tome and Principe', code: 'ST'}, 
+  {name: 'Saudi Arabia', code: 'SA'}, 
+  {name: 'Senegal', code: 'SN'}, 
+  {name: 'Serbia and Montenegro', code: 'CS'}, 
+  {name: 'Seychelles', code: 'SC'}, 
+  {name: 'Sierra Leone', code: 'SL'}, 
+  {name: 'Singapore', code: 'SG'}, 
+  {name: 'Slovakia', code: 'SK'}, 
+  {name: 'Slovenia', code: 'SI'}, 
+  {name: 'Solomon Islands', code: 'SB'}, 
+  {name: 'Somalia', code: 'SO'}, 
+  {name: 'South Africa', code: 'ZA'}, 
+  {name: 'South Georgia and the South Sandwich Islands', code: 'GS'}, 
+  {name: 'Spain', code: 'ES'}, 
+  {name: 'Sri Lanka', code: 'LK'}, 
+  {name: 'Sudan', code: 'SD'}, 
+  {name: 'Suriname', code: 'SR'}, 
+  {name: 'Svalbard and Jan Mayen', code: 'SJ'}, 
+  {name: 'Swaziland', code: 'SZ'}, 
+  {name: 'Sweden', code: 'SE'}, 
+  {name: 'Switzerland', code: 'CH'}, 
+  {name: 'Syrian Arab Republic', code: 'SY'}, 
+  {name: 'Taiwan, Province of China', code: 'TW'}, 
+  {name: 'Tajikistan', code: 'TJ'}, 
+  {name: 'Tanzania, United Republic of', code: 'TZ'}, 
+  {name: 'Thailand', code: 'TH'}, 
+  {name: 'Timor-Leste', code: 'TL'}, 
+  {name: 'Togo', code: 'TG'}, 
+  {name: 'Tokelau', code: 'TK'}, 
+  {name: 'Tonga', code: 'TO'}, 
+  {name: 'Trinidad and Tobago', code: 'TT'}, 
+  {name: 'Tunisia', code: 'TN'}, 
+  {name: 'Turkey', code: 'TR'}, 
+  {name: 'Turkmenistan', code: 'TM'}, 
+  {name: 'Turks and Caicos Islands', code: 'TC'}, 
+  {name: 'Tuvalu', code: 'TV'}, 
+  {name: 'Uganda', code: 'UG'}, 
+  {name: 'Ukraine', code: 'UA'}, 
+  {name: 'United Arab Emirates', code: 'AE'}, 
+  {name: 'United Kingdom', code: 'GB'}, 
+  {name: 'United States', code: 'US'}, 
+  {name: 'United States Minor Outlying Islands', code: 'UM'}, 
+  {name: 'Uruguay', code: 'UY'}, 
+  {name: 'Uzbekistan', code: 'UZ'}, 
+  {name: 'Vanuatu', code: 'VU'}, 
+  {name: 'Venezuela', code: 'VE'}, 
+  {name: 'Viet Nam', code: 'VN'}, 
+  {name: 'Virgin Islands, British', code: 'VG'}, 
+  {name: 'Virgin Islands, U.S.', code: 'VI'}, 
+  {name: 'Wallis and Futuna', code: 'WF'}, 
+  {name: 'Western Sahara', code: 'EH'}, 
+  {name: 'Yemen', code: 'YE'}, 
+  {name: 'Zambia', code: 'ZM'}, 
+  {name: 'Zimbabwe', code: 'ZW'} 
+];

+ 90 - 0
demo/webwidget/widgets/demohtmlwidgets.pp

@@ -152,11 +152,99 @@ Type
     class function WebWidgetClass: TCustomWebWidgetClass; override;
   end;
 
+  { TEventTableWidgetDemo }
+
+  TEventTableWidgetDemo = Class(TDemoContainer)
+  private
+    procedure DoGetCellData(Sender: TObject; Enum: TTableRowEnumerator; aCell: TTableWidgetCellData);
+  Public
+    class function WebWidgetClass: TCustomWebWidgetClass; override;
+    Procedure ShowDemo; override;
+  end;
+
+  { TStringsTableWidgetDemo }
+
+  TStringsTableWidgetDemo = Class(TDemoContainer)
+  Public
+    class function WebWidgetClass: TCustomWebWidgetClass; override;
+    Procedure ShowDemo; override;
+  end;
+
 
 implementation
 
 uses democonsts;
 
+{
+  Countrycodes are included as a JSON javascript definition in the HTML file, countrycodes.js
+  We define it here as an external.
+}
+
+{$modeswitch externalclass}
+
+Type
+  TCountry = Class external name 'Object' (TJSObject)
+    name,code : string;
+  end;
+
+Var
+  CountryCodes : Array of TCountry; external name 'countrycodes';
+
+{ TStringsTableWidgetDemo }
+
+class function TStringsTableWidgetDemo.WebWidgetClass: TCustomWebWidgetClass;
+begin
+  Result:=TStringsTableWidget;
+end;
+
+procedure TStringsTableWidgetDemo.ShowDemo;
+
+Var
+  STW : TStringsTableWidget;
+  I : Integer;
+
+begin
+  inherited ShowDemo;
+  STW:=TStringsTableWidget(WidgetInstance);
+  STW.Classes:='table table-bordered table-striped table-hover table-sm';
+  STW.CustomColumns.Add('ISO');
+  STW.CustomColumns.Add('Country');
+  STW.BeginUpdate;
+  STW.RowCount:=Length(CountryCodes);
+  For I:=0 to Length(CountryCodes)-1 do
+    begin
+    STW.Cells[0,I]:=CountryCodes[i].code;
+    STW.Cells[1,i]:=CountryCodes[i].Name;
+    end;
+  STW.EndUpdate;
+end;
+
+{ TEventTableWidgetDemo }
+
+procedure TEventTableWidgetDemo.DoGetCellData(Sender: TObject; Enum: TTableRowEnumerator; aCell: TTableWidgetCellData);
+
+begin
+  if aCell.Kind=rkBody then
+    case aCell.Col of
+      0 : aCell.Text:='Value '+IntToStr(aCell.Row+1);
+      1 : aCell.Widget:=TTextInputWidget.Create(Self);
+    end;
+end;
+
+class function TEventTableWidgetDemo.WebWidgetClass: TCustomWebWidgetClass;
+begin
+  Result:=TEventTableWidget;
+end;
+
+procedure TEventTableWidgetDemo.ShowDemo;
+begin
+  inherited ShowDemo;
+  TEventTableWidget(WidgetInstance).OnGetCellData:=@DoGetCellData;
+  TEventTableWidget(WidgetInstance).CustomColumns.Add('Name');
+  TEventTableWidget(WidgetInstance).CustomColumns.Add('Value');
+  TEventTableWidget(WidgetInstance).RowCount:=10;
+end;
+
 { TVideoWidgetDemo }
 
 class function TVideoWidgetDemo.WebWidgetClass: TCustomWebWidgetClass;
@@ -424,5 +512,7 @@ initialization
   TSelectWidgetDemo.RegisterDemo;
   TAudioWidgetDemo.RegisterDemo;
   TVideoWidgetDemo.RegisterDemo;
+  TEventTableWidgetDemo.RegisterDemo;
+  TStringsTableWidgetDemo.RegisterDemo;
 end.
 

+ 1 - 0
demo/webwidget/widgets/widgetsdemo.html

@@ -15,6 +15,7 @@
   <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN" crossorigin="anonymous"></script>
   <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js" integrity="sha384-B4gt1jrGC7Jh4AgTPSdUtOBvfO8shuf57BaghqFfPlYxofvL8/KUEfYiJOMMV+rV" crossorigin="anonymous"></script>
   <script src="webwidgetsdemo.js"></script>
+  <script src="countrycodes.js"></script>
 </head>
 <body>
   <!-- navbar -->

+ 163 - 3
packages/webwidget/htmlwidgets.pp

@@ -688,6 +688,7 @@ Type
     FTableID: String;
     FTag: String;
     FText: String;
+    FWidget: TWebWidget;
   Protected
     Procedure SetRowColKind(aRow,aCol : Integer; aKind : TRowKind); virtual;
     Procedure Reset; // do not reset row,col, column
@@ -703,6 +704,7 @@ Type
     Property Text : String Read FText Write FText;
     Property AsHTML : Boolean Read FAsHTML Write FAsHTML;
     Property Content : TJSHTMLElement Read FContent Write FContent;
+    Property Widget : TWebWidget Read FWidget Write FWidget;
     Property TableID : String Read FTableID;
   end;
 
@@ -742,10 +744,11 @@ Type
     FOnRowClick: THTMLNotifyEvent;
     FTableOptions: TTableOptions;
     FOnGetCellData : TOnCellDataEvent;
+    FWidgets : Array of TWebWidget;
+    FUpdateCount : Integer;
     procedure SetCaption(AValue: String);
     procedure SetColumns(AValue: TCustomTableColumns);
     procedure SetTableOptions(AValue: TTableOptions);
-  Public
   Protected
     procedure AppendCaption(aCaptionElement: TJSHTMLElement); virtual;
     procedure RenderData(aElement: TJSHTMLElement); virtual;
@@ -781,11 +784,71 @@ Type
   Public
     Constructor Create(aOwner : TComponent); override;
     Destructor Destroy; override;
+    Procedure BeginUpdate;
+    Procedure EndUpdate;
     Procedure RefreshBody;
   end;
 
-  TTableWidget = Class(TCustomTableWidget)
+  { TEventTableWidget }
+
+  TEventTableWidget = Class(TCustomTableWidget)
+  private
+    FRowCount: Integer;
+    procedure SetRowCount(AValue: Integer);
+  Protected
+    Function GetBodyRowEnumerator : TTableRowEnumerator; override;
+  Published
+    Property RowCount : Integer Read FRowCount Write SetRowCount;
+    Property CustomColumns;
+    Property TableOptions;
+    Property Caption;
+    Property OnGetCellData;
+    Property OnCellClick;
+    Property OnHeaderCellClick;
+    Property OnFooterCellClick;
+    Property OnRowClick;
+    Property OnHeaderRowClick;
+    Property OnFooterRowClick;
+  end;
+
+  { TCustomStringsTableWidget }
+
+  TCustomStringsTableWidget = Class(TCustomTableWidget)
+  private
+    Type
+      TRow = Array of string;
+  private
+    FRows : Array of TRow;
+    function GetRowCount: Integer;
+    procedure SetRowCount(AValue: Integer);
+  Protected
+    Type
+      TStringRowsEnumerator = Class(TTableRowCountEnumerator)
+        Procedure GetCellData(aCell : TTableWidgetCellData); override;
+      end;
+  Protected
+    Procedure CheckIndex(aCol,aRow : Integer);
+    function GetCell(aCol, aRow : integer): String;
+    procedure SetCell(aCol, aRow : integer; AValue: String);
+    Function GetBodyRowEnumerator : TTableRowEnumerator; override;
   Public
+    Property RowCount : Integer Read GetRowCount Write SetRowCount;
+    Property Cells[aCol,aRow : integer] : String Read GetCell Write SetCell;
+    Property CustomColumns;
+    Property TableOptions;
+    Property Caption;
+    Property OnGetCellData;
+    Property OnCellClick;
+    Property OnHeaderCellClick;
+    Property OnFooterCellClick;
+    Property OnRowClick;
+    Property OnHeaderRowClick;
+    Property OnFooterRowClick;
+  end;
+
+  TStringsTableWidget = Class(TCustomStringsTableWidget)
+  Published
+    Property RowCount;
     Property CustomColumns;
     Property TableOptions;
     Property Caption;
@@ -954,6 +1017,9 @@ uses DateUtils;
 
 resourcestring
   SErrInvalidIndex = 'Index %d not in valid range of [0..%d]';
+  SErrInvalidRowCount = 'Invalid row count: %d';
+  SRow = 'Row';
+  SCol = 'Column';
 
 Function ViewPort : TViewPort;
 
@@ -966,6 +1032,77 @@ end;
 Const
   CellTags : Array[TRowKind] of string = ('th','td','td');
 
+{ TEventTableWidget }
+
+procedure TEventTableWidget.SetRowCount(AValue: Integer);
+begin
+  if FRowCount=AValue then Exit;
+  BeginUpdate;
+  FRowCount:=AValue;
+  EndUpdate;
+end;
+
+function TEventTableWidget.GetBodyRowEnumerator: TTableRowEnumerator;
+begin
+  Result:=TTableRowCountEnumerator.Create(Self,RowCount);
+end;
+
+{ TCustomStringsTableWidget.TStringRowsEnumerator }
+
+procedure TCustomStringsTableWidget.TStringRowsEnumerator.GetCellData(aCell: TTableWidgetCellData);
+begin
+  aCell.Text:=TCustomStringsTableWidget(Table).Cells[aCell.Col,aCell.Row];
+end;
+
+{ TCustomStringsTableWidget }
+
+function TCustomStringsTableWidget.GetCell(aCol, aRow : integer): String;
+begin
+  CheckIndex(aCol,aRow);
+  Result:=FRows[aRow][aCol];
+end;
+
+function TCustomStringsTableWidget.GetRowCount: Integer;
+begin
+  Result:=Length(FRows);
+end;
+
+procedure TCustomStringsTableWidget.SetCell(aCol, aRow : integer; AValue: String);
+begin
+  CheckIndex(aCol,aRow);
+  BeginUpdate;
+  try
+    FRows[aRow][aCol]:=AValue;
+  Finally
+    EndUpdate;
+  end;
+end;
+
+function TCustomStringsTableWidget.GetBodyRowEnumerator: TTableRowEnumerator;
+begin
+  Result:=TStringRowsEnumerator.Create(Self,RowCount);
+end;
+
+procedure TCustomStringsTableWidget.SetRowCount(AValue: Integer);
+begin
+  if AValue<0 then
+     raise EWidgets.CreateFmt(SerrInvalidRowCount, [aValue]);
+  BeginUpdate;
+  try
+    SetLength(FRows,aValue);
+  Finally
+    EndUpdate;
+  end;
+end;
+
+procedure TCustomStringsTableWidget.CheckIndex(aCol, aRow: Integer);
+begin
+  If (aCol<0) or (aCol>=CustomColumns.Count) then
+    Raise EWidgets.CreateFmt(SCol+' '+SErrInvalidIndex,[aCol,0,CustomColumns.Count-1]);
+  If (aRow<0) or (aRow>=RowCount) then
+    Raise EWidgets.CreateFmt(SRow+' '+SErrInvalidIndex,[aRow,0,RowCount-1]);
+end;
+
 { TTagWidget }
 
 constructor TTagWidget.Create(aOwner: TComponent);
@@ -1203,6 +1340,7 @@ begin
   FText:='';
   FContent:=Nil;
   FAsHTML:=False;
+  FWidget:=Nil;
 end;
 
 constructor TTableWidgetCellData.Create(aTable: TCustomTableWidget; aTableID: String);
@@ -1344,11 +1482,13 @@ Var
   elID : string;
 begin
   K:=aCell.Kind;
-  if toCellID in TableOptions then
+  if (toCellID in TableOptions) or Assigned(aCell.Widget) then
     elID:=aCell.TableID+'-'+RowKindNames[K]+'-'+IntToStr(ACell.Row)+'-'+IntToStr(aCell.Col)
   else
     elID:='';
   C:=CreateElement(aCell.Tag,elID);
+  if aCell.Widget<>Nil then
+    aCell.Widget.ParentID:=elID;
   if aCell.Content<>Nil then
     C.AppendChild(aCell.Content)
   else if aCell.AsHTML then
@@ -1390,6 +1530,8 @@ begin
      end;
   if Assigned(M) then
     C.OnClick:=M;
+  if aCell.Widget<>Nil then
+    TJSArray(FWidgets).Push(aCell.Widget);
   Result:=C;
 end;
 
@@ -1484,8 +1626,10 @@ procedure TCustomTableWidget.RenderData(aElement: TJSHTMLElement);
 Var
   El : TJSHTMLElement;
   aCell : TTableWidgetCellData;
+  W : TWebWidget;
 
 begin
+  FWidgets:=[];
   if (Caption<>'') then
     begin
     El:=CreateElement('caption',aElement.ID+'-caption');
@@ -1528,6 +1672,9 @@ begin
     aCell.SetRowColKind(-1,-1,rkFooter);
     RenderRows(El,rkFooter,aCell);
     end;
+  for W in FWidgets do
+    W.Refresh;
+  FWidgets:=[];
 end;
 
 function TCustomTableWidget.CreateCellData(const aTableID : String): TTableWidgetCellData;
@@ -1564,6 +1711,19 @@ begin
   inherited Destroy;
 end;
 
+procedure TCustomTableWidget.BeginUpdate;
+begin
+  Inc(FUpDateCount);
+end;
+
+procedure TCustomTableWidget.EndUpdate;
+begin
+  if (FUpdateCount>0) then
+    Dec(FUpDateCount);
+  if (FUpdateCount=0) and IsRendered then
+    Refresh;
+end;
+
 procedure TCustomTableWidget.RefreshBody;
 begin
   if Not Assigned(Element) then