1 /* 2 * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.javafx.webkit.theme; 27 28 import java.lang.ref.WeakReference; 29 30 import com.sun.javafx.logging.PlatformLogger; 31 import com.sun.javafx.logging.PlatformLogger.Level; 32 import javafx.beans.Observable; 33 import javafx.geometry.Bounds; 34 import javafx.geometry.Orientation; 35 import javafx.scene.Node; 36 import javafx.scene.control.Control; 37 import javafx.scene.control.ScrollBar; 38 39 import com.sun.webkit.graphics.Ref; 40 import com.sun.webkit.graphics.ScrollBarTheme; 41 import com.sun.webkit.graphics.WCGraphicsContext; 42 import com.sun.javafx.webkit.Accessor; 43 import com.sun.javafx.webkit.theme.RenderThemeImpl.Pool; 44 import com.sun.javafx.webkit.theme.RenderThemeImpl.ViewListener; 45 import com.sun.webkit.graphics.WCSize; 46 47 public final class ScrollBarThemeImpl extends ScrollBarTheme { 48 49 private final static PlatformLogger log = PlatformLogger.getLogger(ScrollBarThemeImpl.class.getName()); 50 51 private WeakReference<ScrollBar> testSBRef = // used for scrollbar thickness calculation 52 new WeakReference<ScrollBar>(null); 53 54 private final Accessor accessor; 55 56 private final Pool<ScrollBarWidget> pool; 57 58 private static final class ScrollBarRef extends Ref { 59 private final WeakReference<ScrollBarWidget> sbRef; 60 61 private ScrollBarRef(ScrollBarWidget sb) { 62 this.sbRef = new WeakReference<ScrollBarWidget>(sb); 63 } 64 65 private Control asControl() { 66 return sbRef.get(); 67 } 68 } 69 70 /* 71 * Note, the class should be instantiated no later than 72 * the appropriate page is created to ensure 'testSB' 73 * is added to the view before paiting starts. 74 */ 75 public ScrollBarThemeImpl(final Accessor accessor) { 76 this.accessor = accessor; 77 pool = new Pool<ScrollBarWidget>( 78 sb -> { 79 accessor.removeChild(sb); 80 }, ScrollBarWidget.class); 81 accessor.addViewListener(new ViewListener(pool, accessor) { 82 @Override public void invalidated(Observable ov) { 83 super.invalidated(ov); 84 ScrollBar testSB = new ScrollBarWidget(ScrollBarThemeImpl.this); 85 // testSB should be added to the new WebView (if any) 86 accessor.addChild(testSB); 87 testSBRef = new WeakReference<ScrollBar>(testSB); 88 } 89 }); 90 91 } 92 93 ScrollBar getTestSBRef() { 94 return testSBRef.get(); 95 } 96 97 private static Orientation convertOrientation(int orientation) { 98 return orientation == VERTICAL_SCROLLBAR ? Orientation.VERTICAL : Orientation.HORIZONTAL; 99 } 100 101 private void adjustScrollBar(ScrollBar sb, int w, int h, int orientation) { 102 Orientation current = convertOrientation(orientation); 103 if (current != sb.getOrientation()) { 104 sb.setOrientation(current); 105 } 106 107 if (current == Orientation.VERTICAL) { 108 w = ScrollBarTheme.getThickness(); 109 } else { 110 h = ScrollBarTheme.getThickness(); 111 } 112 113 if ((w != sb.getWidth()) || (h != sb.getHeight())) { 114 sb.resize(w, h); 115 } 116 } 117 118 private void adjustScrollBar(ScrollBar sb, int w, int h, int orientation, 119 int value, int visibleSize, int totalSize) 120 { 121 adjustScrollBar(sb, w, h, orientation); 122 boolean disable = totalSize <= visibleSize; 123 sb.setDisable(disable); 124 if (disable) { 125 return; 126 } 127 if (value < 0) { 128 value = 0; 129 } else if(value > (totalSize - visibleSize)) { 130 value = totalSize - visibleSize; 131 } 132 133 if (sb.getMax() != totalSize || sb.getVisibleAmount() != visibleSize) { 134 sb.setValue(0); // reset 'value' to let 'max' & 'visibleAmount' be reinitialized 135 sb.setMax(totalSize); 136 sb.setVisibleAmount(visibleSize); 137 } 138 139 // For FX ScrollBar the following is true: 140 // [min <= value <= max] & [min <= visibleAmount <= max] 141 // But webkit assumes that: 142 // [0 <= value <= totalSize - visibleAmount] 143 // So, we calculate a factor from the following equation: 144 // (totalSize - visibleSize) * factor = totalSize 145 if (totalSize > visibleSize) { 146 float factor = ((float)totalSize) / (totalSize - visibleSize); 147 if (sb.getValue() != value * factor) { 148 sb.setValue(value * factor); // eventually set 'value' 149 } 150 } 151 } 152 153 @Override protected Ref createWidget(long id, int w, int h, int orientation, 154 int value, int visibleSize, 155 int totalSize) 156 { 157 ScrollBarWidget sb = pool.get(id); 158 if (sb == null) { 159 sb = new ScrollBarWidget(this); 160 pool.put(id, sb, accessor.getPage().getUpdateContentCycleID()); 161 accessor.addChild(sb); 162 } 163 adjustScrollBar(sb, w, h, orientation, value, visibleSize, totalSize); 164 return new ScrollBarRef(sb); 165 } 166 167 @Override public void paint(WCGraphicsContext g, Ref sbRef, 168 int x, int y, int pressedPart, int hoveredPart) 169 { 170 ScrollBar sb = (ScrollBar)((ScrollBarRef)sbRef).asControl(); 171 if (sb == null) { 172 return; 173 } 174 175 if (log.isLoggable(Level.FINEST)) { 176 log.finest("[{0}, {1} {2}x{3}], {4}", 177 new Object[] {x, y, sb.getWidth(), sb.getHeight(), 178 sb.getOrientation() == Orientation.VERTICAL ? "VERTICAL" : "HORIZONTAL"}); 179 } 180 g.saveState(); 181 g.translate(x, y); 182 Renderer.getRenderer().render(sb, g); 183 g.restoreState(); 184 } 185 186 @Override public WCSize getWidgetSize(Ref widget) { 187 ScrollBar sb = (ScrollBar)((ScrollBarRef)widget).asControl(); 188 if (sb != null) { 189 return new WCSize((float)sb.getWidth(), (float)sb.getHeight()); 190 } 191 return new WCSize(0, 0); 192 } 193 194 @Override protected void getScrollBarPartRect(long id, int part, int rect[]) { 195 ScrollBar sb = pool.get(id); 196 if (sb == null) { 197 return; 198 } 199 200 Node node = null; 201 if (part == FORWARD_BUTTON_START_PART) { 202 node = getIncButton(sb); 203 } else if (part == BACK_BUTTON_START_PART) { 204 node = getDecButton(sb); 205 } else if (part == TRACK_BG_PART) { 206 node = getTrack(sb); 207 } 208 209 assert rect.length >= 4; 210 if (node != null) { 211 Bounds bounds = node.getBoundsInParent(); 212 rect[0] = (int)bounds.getMinX(); 213 rect[1] = (int)bounds.getMinY(); 214 rect[2] = (int)bounds.getWidth(); 215 rect[3] = (int)bounds.getHeight(); 216 } else { 217 rect[0] = rect[1] = rect[2] = rect[3] = 0; 218 } 219 log.finest("id {0} part {1} bounds {2},{3} {4}x{5}", 220 new Object[] {String.valueOf(id), String.valueOf(part), rect[0], rect[1], rect[2], rect[3]}); 221 } 222 223 private static Node getTrack(ScrollBar scrollBar) { 224 // return ((ScrollBarSkin)scrollBar.getSkin()).getTrack(); 225 return findNode(scrollBar, "track"); 226 } 227 228 private static Node getIncButton(ScrollBar scrollBar) { 229 // return ((ScrollBarSkin)scrollBar.getSkin()).getIncrementButton(); 230 return findNode(scrollBar, "increment-button"); 231 } 232 233 private static Node getDecButton(ScrollBar scrollBar) { 234 // return ((ScrollBarSkin)scrollBar.getSkin()).getDecrementButton(); 235 return findNode(scrollBar, "decrement-button"); 236 } 237 238 private static Node findNode(ScrollBar scrollBar, String styleclass) { 239 for (Node n : scrollBar.getChildrenUnmodifiable()) { 240 if (n.getStyleClass().contains(styleclass)) { 241 return n; 242 } 243 } 244 return null; 245 } 246 }