001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2016 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.client.data; 021 022import com.google.gwt.cell.client.Cell.Context; 023import com.google.gwt.core.client.Scheduler; 024import com.google.gwt.dom.client.Style; 025import com.google.gwt.dom.client.Style.Cursor; 026import com.google.gwt.dom.client.Style.Display; 027import com.google.gwt.safehtml.shared.SafeHtmlBuilder; 028import gwt.material.design.client.base.constants.StyleName; 029import gwt.material.design.client.base.constants.TableCssName; 030import gwt.material.design.client.constants.HideOn; 031import gwt.material.design.client.constants.IconSize; 032import gwt.material.design.client.constants.IconType; 033import gwt.material.design.client.constants.TextAlign; 034import gwt.material.design.client.constants.WavesType; 035import gwt.material.design.client.data.component.CategoryComponent; 036import gwt.material.design.client.data.component.Component; 037import gwt.material.design.client.data.component.RowComponent; 038import gwt.material.design.client.ui.MaterialCheckBox; 039import gwt.material.design.client.ui.MaterialIcon; 040import gwt.material.design.client.ui.html.Div; 041import gwt.material.design.client.ui.table.TableData; 042import gwt.material.design.client.ui.table.TableHeader; 043import gwt.material.design.client.ui.table.TableRow; 044import gwt.material.design.client.ui.table.TableSubHeader; 045import gwt.material.design.client.ui.table.cell.Column; 046import gwt.material.design.client.ui.table.cell.WidgetColumn; 047 048import java.util.List; 049import java.util.Map; 050import java.util.logging.Logger; 051 052/** 053 * Base Component Renderer used by {@link AbstractDataView}. 054 * <br><br> 055 * This can be extended upon or can be replaced all together. 056 * 057 * @author Ben Dol 058 */ 059public class BaseRenderer<T> implements Renderer<T> { 060 061 private final Logger logger = Logger.getLogger(BaseRenderer.class.getName()); 062 063 // Configurations 064 private int calculatedRowHeight = 55; 065 private int expectedRowHeight = calculatedRowHeight; 066 067 private IconType sortAscIcon = IconType.ARROW_UPWARD; 068 private IconType sortDescIcon = IconType.ARROW_DOWNWARD; 069 protected IconSize sortIconSize = IconSize.TINY; 070 071 @Override 072 public void copy(Renderer<T> renderer) { 073 expectedRowHeight = renderer.getExpectedRowHeight(); 074 calculatedRowHeight = renderer.getCalculatedRowHeight(); 075 // Right now we only copy the height data 076 /*sortAscIcon = renderer.getSortAscIcon(); 077 sortDescIcon = renderer.getSortDescIcon(); 078 sortIconSize = renderer.getSortIconSize();*/ 079 } 080 081 @Override 082 public TableRow drawRow(DataView<T> dataView, RowComponent<T> rowComponent, Object valueKey, 083 List<Column<T, ?>> columns, boolean redraw) { 084 T data = rowComponent.getData(); 085 TableRow row = rowComponent.getWidget(); 086 boolean draw = true; 087 if(row == null) { 088 // Create a new row element 089 row = new TableRow(); 090 Style style = row.getElement().getStyle(); 091 style.setDisplay(Display.NONE); 092 style.setProperty("height", getExpectedRowHeight() + "px"); 093 style.setProperty("maxHeight", getExpectedRowHeight() + "px"); 094 style.setProperty("minHeight", getExpectedRowHeight() + "px"); 095 row.setStyleName(TableCssName.DATA_ROW); 096 rowComponent.setWidget(row); 097 098 if(!dataView.getSelectionType().equals(SelectionType.NONE)) { 099 TableData selection = drawSelectionCell(); 100 row.add(selection); 101 } 102 } else if(!redraw && !rowComponent.isRedraw()) { 103 draw = false; 104 } 105 106 if(draw) { 107 // Build the columns 108 int colOffset = dataView.getColumnOffset(); 109 int colSize = columns.size(); 110 111 for(int c = 0; c < colSize; c++) { 112 int colIndex = c + colOffset; 113 Context context = new Context(rowComponent.getIndex(), colIndex, valueKey); 114 drawColumn(row, context, data, columns.get(c), colIndex, dataView.isHeaderVisible(colIndex)); 115 } 116 rowComponent.setRedraw(false); 117 } 118 119 if(dataView.isUseRowExpansion()) { 120 if(!row.hasExpansionColumn()) { 121 TableData expand = new TableData(); 122 expand.setId("colex"); 123 MaterialIcon expandIcon = new MaterialIcon(); 124 expandIcon.setId("expand"); 125 expandIcon.setWidth("100%"); 126 expandIcon.setIconType(IconType.KEYBOARD_ARROW_DOWN); 127 expandIcon.setWaves(WavesType.LIGHT); 128 expandIcon.getElement().getStyle().setCursor(Cursor.POINTER); 129 expand.add(expandIcon); 130 row.add(expand); 131 } 132 } else if(row.hasExpansionColumn()) { 133 row.removeExpansionColumn(); 134 } 135 136 Scheduler.get().scheduleDeferred(() -> { 137 calculateRowHeight(rowComponent); 138 }); 139 return row; 140 } 141 142 @Override 143 public TableSubHeader drawCategory(CategoryComponent category) { 144 if(category != null) { 145 TableSubHeader subHeader = category.getWidget(); 146 if(subHeader == null) { 147 subHeader = category.render(); 148 assert subHeader != null : "rendered category TableSubHeader cannot be null."; 149 subHeader.setHeight(category.getHeight()); 150 } 151 return subHeader; 152 } 153 154 // No subheader was added 155 return null; 156 } 157 158 @Override 159 public TableRow drawCustom(Component<?> component) { 160 return new TableRow(); 161 } 162 163 @Override 164 public TableData drawSelectionCell() { 165 TableData checkBox = new TableData(); 166 checkBox.setId("col0"); 167 checkBox.setStyleName(TableCssName.SELECTION); 168 new MaterialCheckBox(checkBox.getElement()); 169 checkBox.addClickHandler(event -> { 170 event.getNativeEvent().preventDefault(); 171 }); 172 return checkBox; 173 } 174 175 @Override 176 @SuppressWarnings("unchecked") 177 public TableData drawColumn(TableRow row, Context context, T rowValue, Column<T, ?> column, 178 int beforeIndex, boolean visible) { 179 TableData data = null; 180 if(row != null && rowValue != null) { 181 data = row.getColumn(beforeIndex); 182 if(data == null) { 183 data = new TableData(); 184 row.insert(data, beforeIndex); 185 } else { 186 data.clear(); 187 } 188 189 Div wrapper = new Div(); 190 191 // Render the column cell 192 if(column instanceof WidgetColumn) { 193 wrapper.setStyleName(TableCssName.WIDGET_CELL); 194 wrapper.add(((WidgetColumn) column).render(context, rowValue)); 195 } else { 196 SafeHtmlBuilder sb = new SafeHtmlBuilder(); 197 column.render(context, rowValue, sb); 198 wrapper.getElement().setInnerHTML(sb.toSafeHtml().asString()); 199 wrapper.setStyleName(TableCssName.CELL); 200 } 201 202 data.add(wrapper); 203 204 data.setId("col" + beforeIndex); 205 data.setDataTitle(column.getName()); 206 HideOn hideOn = column.getHideOn(); 207 if(hideOn != null) { 208 data.setHideOn(hideOn); 209 } 210 TextAlign textAlign = column.getTextAlign(); 211 if(textAlign != null) { 212 data.setTextAlign(textAlign); 213 } 214 if(column.isNumeric()) { 215 data.addStyleName(TableCssName.NUMERIC); 216 } 217 218 // Apply the style properties 219 Style style = data.getElement().getStyle(); 220 Map<StyleName, String> styleProps = column.getStyleProperties(); 221 if(styleProps != null) { 222 styleProps.forEach((s, v) -> style.setProperty(s.styleName(), v)); 223 } 224 225 // Hide if defined as not visible 226 // This can be the case when a header is toggled off. 227 if(!visible) { 228 data.$this().hide(); 229 } 230 } 231 return data; 232 } 233 234 @Override 235 public TableHeader drawColumnHeader(Column<T, ?> column, String header, int index) { 236 MaterialIcon sortIcon = new MaterialIcon(); 237 sortIcon.setIconSize(sortIconSize); 238 239 TableHeader th = new TableHeader(sortIcon); 240 th.setId("col" + index); 241 th.setHeader(header); 242 HideOn hideOn = column.getHideOn(); 243 if(hideOn != null) { 244 th.setHideOn(hideOn); 245 } 246 TextAlign textAlign = column.getTextAlign(); 247 if(textAlign != null) { 248 th.setTextAlign(textAlign); 249 } 250 if(column.isNumeric()) { 251 th.addStyleName(TableCssName.NUMERIC); 252 } 253 254 // Apply the style properties 255 Style style = th.getElement().getStyle(); 256 Map<StyleName, String> styleProps = column.getStyleProperties(); 257 if(styleProps != null) { 258 styleProps.forEach((s, v) -> style.setProperty(s.styleName(), v)); 259 } 260 261 // Set the headers width 262 String width = column.getWidth(); 263 if(width != null) { 264 th.setWidth(width); 265 } 266 th.setVisible(true); 267 return th; 268 } 269 270 @Override 271 public void drawSortIcon(TableHeader th, SortContext<T> sortContext) { 272 if(sortContext.getSortDir().equals(SortDir.ASC)) { 273 th.getSortIcon().setIconType(sortAscIcon); 274 } else { 275 th.getSortIcon().setIconType(sortDescIcon); 276 } 277 } 278 279 @Override 280 public int getExpectedRowHeight() { 281 return expectedRowHeight; 282 } 283 284 @Override 285 public void setExpectedRowHeight(int expectedRowHeight) { 286 if(expectedRowHeight < 33) { 287 logger.warning("Expected row height must be 33px or higher, setting row height to 33px."); 288 this.expectedRowHeight = 33; 289 } else { 290 this.expectedRowHeight = expectedRowHeight; 291 } 292 } 293 294 @Override 295 public void calculateRowHeight(RowComponent<T> row) { 296 TableRow element = row.getWidget(); 297 if(element != null) { 298 int rowHeight = element.$this().outerHeight(true); 299 if (rowHeight > 0 && rowHeight != calculatedRowHeight) { 300 calculatedRowHeight = rowHeight; 301 } 302 } 303 } 304 305 @Override 306 public int getCalculatedRowHeight() { 307 return calculatedRowHeight; 308 } 309 310 @Override 311 public IconType getSortAscIcon() { 312 return sortAscIcon; 313 } 314 315 @Override 316 public void setSortAscIcon(IconType sortAscIcon) { 317 this.sortAscIcon = sortAscIcon; 318 } 319 320 @Override 321 public IconType getSortDescIcon() { 322 return sortDescIcon; 323 } 324 325 @Override 326 public void setSortDescIcon(IconType sortDescIcon) { 327 this.sortDescIcon = sortDescIcon; 328 } 329 330 @Override 331 public IconSize getSortIconSize() { 332 return sortIconSize; 333 } 334 335 @Override 336 public void setSortIconSize(IconSize sortIconSize) { 337 this.sortIconSize = sortIconSize; 338 } 339}