001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.ui; 029 030import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 031import org.opencms.gwt.client.util.CmsPositionBean; 032import org.opencms.gwt.client.util.CmsStyleVariable; 033 034import java.util.ArrayList; 035import java.util.Collections; 036import java.util.List; 037 038import com.google.gwt.core.client.GWT; 039import com.google.gwt.dom.client.DivElement; 040import com.google.gwt.dom.client.Document; 041import com.google.gwt.dom.client.Element; 042import com.google.gwt.dom.client.Node; 043import com.google.gwt.dom.client.NodeList; 044import com.google.gwt.dom.client.Style; 045import com.google.gwt.dom.client.Style.Unit; 046import com.google.gwt.uibinder.client.UiBinder; 047import com.google.gwt.uibinder.client.UiField; 048import com.google.gwt.user.client.Window; 049import com.google.gwt.user.client.ui.Composite; 050import com.google.gwt.user.client.ui.HTML; 051 052import elemental2.dom.DomGlobal; 053import jsinterop.base.Js; 054 055/** 056 * A Widget to display a highlighting border around a specified position.<p> 057 * 058 * @since 8.0.0 059 */ 060public class CmsHighlightingBorder extends Composite { 061 062 /** Enumeration of available border colours. */ 063 public enum BorderColor { 064 065 /** Color blue. */ 066 blue(I_CmsLayoutBundle.INSTANCE.highlightCss().colorBlue()), 067 068 /** Color grey. */ 069 grey(I_CmsLayoutBundle.INSTANCE.highlightCss().colorGrey()), 070 071 /** Color red. */ 072 red(I_CmsLayoutBundle.INSTANCE.highlightCss().colorRed()), 073 074 /** Solid grey. */ 075 solidGrey(I_CmsLayoutBundle.INSTANCE.highlightCss().colorSolidGrey()); 076 077 /** CSS class used to display the border colour. */ 078 private String m_cssClass; 079 080 /** 081 * Constructor.<p> 082 * 083 * @param cssClass the CSS class to display the border colour 084 */ 085 private BorderColor(String cssClass) { 086 087 m_cssClass = cssClass; 088 } 089 090 /** 091 * Returns the associated CSS class.<p> 092 * 093 * @return the CSS class 094 */ 095 public String getCssClass() { 096 097 return m_cssClass; 098 } 099 } 100 101 /** The ui-binder interface for this composite. */ 102 interface I_CmsHighlightingBorderUiBinder extends UiBinder<HTML, CmsHighlightingBorder> { 103 // GWT interface, nothing to do here 104 } 105 106 /** The default border offset to the given position. */ 107 private static final int BORDER_OFFSET = 4; 108 109 /** The border width. */ 110 private static final int BORDER_WIDTH = 2; 111 112 /** The ui-binder instance. */ 113 private static I_CmsHighlightingBorderUiBinder uiBinder = GWT.create(I_CmsHighlightingBorderUiBinder.class); 114 115 /** Horizontal offset of the midpoint separators. */ 116 public static final int SEPARATOR_OFFSET = 30; 117 118 /** The bottom border. */ 119 @UiField 120 protected DivElement m_borderBottom; 121 122 /** The left border. */ 123 @UiField 124 protected DivElement m_borderLeft; 125 126 /** The right border. */ 127 @UiField 128 protected DivElement m_borderRight; 129 130 /** The element containing the midpoint separators, if any. */ 131 @UiField 132 protected DivElement m_midpoints; 133 134 /** The top border. */ 135 @UiField 136 protected DivElement m_borderTop; 137 138 /** The border offset. */ 139 private int m_borderOffset; 140 141 /** The style variable used to change the color of the border. */ 142 private CmsStyleVariable m_colorVariable; 143 144 /** The positioning parent element. */ 145 private Element m_positioningParent; 146 147 private boolean m_correctTop; 148 149 /** 150 * Constructor.<p> 151 * 152 * @param position the position data 153 * @param color the border color 154 */ 155 public CmsHighlightingBorder(CmsPositionBean position, BorderColor color) { 156 157 this( 158 position.getHeight(), 159 position.getWidth(), 160 position.getLeft(), 161 position.getTop(), 162 color, 163 BORDER_OFFSET, 164 false); 165 } 166 167 /** 168 * Constructor.<p> 169 * 170 * @param position the position data 171 * @param color the border color 172 * @param borderOffset the border offset 173 */ 174 public CmsHighlightingBorder(CmsPositionBean position, BorderColor color, int borderOffset) { 175 176 this( 177 position.getHeight(), 178 position.getWidth(), 179 position.getLeft(), 180 position.getTop(), 181 color, 182 borderOffset, 183 false); 184 } 185 186 /** 187 * Constructor.<p> 188 * 189 * @param positioningParent the element the border is positioned around, position is set relative to it 190 * @param color the border color 191 */ 192 public CmsHighlightingBorder(Element positioningParent, BorderColor color) { 193 194 m_borderOffset = BORDER_OFFSET; 195 initWidget(uiBinder.createAndBindUi(this)); 196 m_colorVariable = new CmsStyleVariable(getWidget()); 197 m_colorVariable.setValue(color.getCssClass()); 198 m_positioningParent = positioningParent; 199 resetPosition(); 200 } 201 202 /** 203 * Constructor.<p> 204 * 205 * @param height the height 206 * @param width the width 207 * @param positionLeft the absolute left position 208 * @param positionTop the absolute top position 209 * @param color the border color 210 * @param borderOffset the border offset 211 */ 212 public CmsHighlightingBorder( 213 int height, 214 int width, 215 int positionLeft, 216 int positionTop, 217 BorderColor color, 218 int borderOffset, 219 boolean correctTop) { 220 221 m_borderOffset = borderOffset; 222 initWidget(uiBinder.createAndBindUi(this)); 223 m_colorVariable = new CmsStyleVariable(getWidget()); 224 m_colorVariable.setValue(color.getCssClass()); 225 m_correctTop = correctTop; 226 setPosition(height, width, positionLeft, positionTop); 227 } 228 229 /** 230 * Enables the border animation.<p> 231 * (Is enabled by default)<p> 232 * 233 * @param animated <code>true</code> to enable border animation 234 */ 235 public void enableAnimation(boolean animated) { 236 237 if (animated) { 238 addStyleName(I_CmsLayoutBundle.INSTANCE.highlightCss().animated()); 239 } else { 240 removeStyleName(I_CmsLayoutBundle.INSTANCE.highlightCss().animated()); 241 } 242 } 243 244 /** 245 * Gets the vertical offsets (relative to the viewport) of the horizontal lines (top, midpoints, bottom, in this order). 246 * 247 * @return the vertical offsets of the horizonral lines 248 */ 249 public List<Integer> getClientVerticalOffsets() { 250 251 List<Integer> result = new ArrayList<>(); 252 List<DivElement> elements = new ArrayList<>(); 253 elements.add(m_borderTop); 254 for (int i = 0; i < m_midpoints.getChildCount(); i++) { 255 elements.add((DivElement)m_midpoints.getChild(i)); 256 } 257 elements.add(m_borderBottom); 258 for (DivElement elem : elements) { 259 elemental2.dom.Element elem0 = Js.cast(elem); 260 int top = (int)Math.round(elem0.getBoundingClientRect().top); 261 result.add(Integer.valueOf(top)); 262 } 263 return result; 264 } 265 266 /** 267 * Hides the border.<p> 268 */ 269 public void hide() { 270 271 setVisible(false); 272 } 273 274 /** 275 * Recalculates the position and dimension when a positioning parent is given.<p> 276 */ 277 public void resetPosition() { 278 279 // fail if no positioning parent given 280 assert m_positioningParent != null; 281 if (m_positioningParent != null) { 282 setPosition(m_positioningParent.getOffsetHeight(), m_positioningParent.getOffsetWidth(), 0, 0); 283 } 284 } 285 286 /** 287 * Sets the color of the border.<p> 288 * 289 * @param color the color of the border 290 */ 291 public void setColor(BorderColor color) { 292 293 m_colorVariable.setValue(color.getCssClass()); 294 } 295 296 /** 297 * Sets the midpoint separators, given a list of their vertical offsets from the top. 298 * 299 * @param verticalOffsets the list of midpoint offsets 300 */ 301 public void setMidpoints(List<Integer> verticalOffsets) { 302 303 m_midpoints.removeAllChildren(); 304 if (verticalOffsets == null) { 305 verticalOffsets = Collections.emptyList(); 306 } 307 for (Integer midpoint : verticalOffsets) { 308 DivElement midpointLine = Document.get().createDivElement(); 309 midpointLine.addClassName(I_CmsLayoutBundle.INSTANCE.highlightCss().midpointSeparator()); 310 midpointLine.getStyle().setTop(midpoint.doubleValue() + BORDER_OFFSET, Unit.PX); 311 midpointLine.getStyle().setLeft(SEPARATOR_OFFSET, Unit.PX); 312 m_midpoints.appendChild(midpointLine); 313 314 } 315 } 316 317 /** 318 * Sets the border position.<p> 319 * 320 * @param position the position data 321 */ 322 public void setPosition(CmsPositionBean position) { 323 324 setPosition(position.getHeight(), position.getWidth(), position.getLeft(), position.getTop()); 325 } 326 327 /** 328 * Sets the border position.<p> 329 * 330 * @param height the height 331 * @param width the width 332 * @param positionLeft the absolute left position 333 * @param positionTop the absolute top position 334 */ 335 public void setPosition(int height, int width, int positionLeft, int positionTop) { 336 337 positionLeft -= m_borderOffset; 338 339 // make sure highlighting does not introduce additional horizontal scroll-bars 340 if ((m_positioningParent == null) && (positionLeft < 0)) { 341 // position left should not be negative 342 width += positionLeft; 343 positionLeft = 0; 344 } 345 width += (2 * m_borderOffset) - BORDER_WIDTH; 346 if ((m_positioningParent == null) 347 && (Window.getClientWidth() < (width + positionLeft)) 348 && (Window.getScrollLeft() == 0)) { 349 // highlighting should not extend over the right hand 350 width = Window.getClientWidth() - (positionLeft + BORDER_WIDTH); 351 } 352 Style style = getElement().getStyle(); 353 style.setLeft(positionLeft, Unit.PX); 354 int correction = m_correctTop 355 ? (int)(DomGlobal.document.body.getBoundingClientRect().top 356 - DomGlobal.document.documentElement.getBoundingClientRect().top) 357 : 0; 358 style.setTop(positionTop - m_borderOffset - correction, Unit.PX); 359 360 setHeight((height + (2 * m_borderOffset)) - BORDER_WIDTH); 361 setWidth(width); 362 } 363 364 /** 365 * Shows the border.<p> 366 */ 367 public void show() { 368 369 setVisible(true); 370 } 371 372 /** 373 * Sets the highlighting height.<p> 374 * 375 * @param height the height 376 */ 377 private void setHeight(int height) { 378 379 m_borderRight.getStyle().setHeight(height, Unit.PX); 380 m_borderLeft.getStyle().setHeight(height, Unit.PX); 381 m_borderBottom.getStyle().setTop(height, Unit.PX); 382 } 383 384 /** 385 * Sets the highlighting width.<p> 386 * 387 * @param width the width 388 */ 389 private void setWidth(int width) { 390 391 m_borderTop.getStyle().setWidth(width + BORDER_WIDTH, Unit.PX); 392 m_borderBottom.getStyle().setWidth(width + BORDER_WIDTH, Unit.PX); 393 m_borderRight.getStyle().setLeft(width, Unit.PX); 394 NodeList<Node> midpoints = m_midpoints.getChildNodes(); 395 for (int i = 0; i < midpoints.getLength(); i++) { 396 DivElement midpoint = (DivElement)midpoints.getItem(i); 397 midpoint.getStyle().setWidth((width + BORDER_WIDTH) - (2 * SEPARATOR_OFFSET), Unit.PX); 398 } 399 } 400 401}