Last active
August 29, 2015 13:56
-
-
Save DemkaAge/9343436 to your computer and use it in GitHub Desktop.
Custom Scroll Bar
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.brbpm.lecm.wm.styles.client.widgets; | |
import com.google.gwt.resources.client.ClientBundle; | |
/** | |
* User: dshahovkin | |
* Date: 28.11.13 | |
* Time: 15:24 | |
*/ | |
public interface CustomScrollPanelResources extends ClientBundle { | |
@Source("ScrollPanel.css") | |
ScrollPanel scrollPanel(); | |
@Source("VerticalScrollbar.css") | |
VerticalScrollbar verticalScrollbar(); | |
@Source("HorizontalScrollbar.css") | |
HorizontalScrollbar horizontalScrollbar(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.viewport { | |
background: #c8dae8; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
-moz-opacity: 0.7; | |
-khtml-opacity: 0.7; | |
-webkit-opacity: 0.7; | |
opacity: 0.7; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=70)'); | |
filter: literal('alpha(opacity=70)'); | |
} | |
.knob { | |
background: #8aa8bf; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
-moz-opacity: 0.7; | |
-khtml-opacity: 0.7; | |
-webkit-opacity: 0.7; | |
opacity: 0.7; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=70)'); | |
filter: literal('alpha(opacity=70)'); | |
-webkit-transition: width 600ms ease 0ms; | |
-moz-transition: width 600ms ease 0ms; | |
-o-transition: width 600ms ease 0ms; | |
transition: width 600ms ease 0ms; | |
} | |
.knob:hover { | |
-moz-opacity: 1; | |
-khtml-opacity: 1; | |
-webkit-opacity: 1; | |
opacity: 1; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=100)'); | |
filter: literal('alpha(opacity=100)'); | |
background: #7a9cb6; | |
} | |
.knob:active { | |
-moz-opacity: 1; | |
-khtml-opacity: 1; | |
-webkit-opacity: 1; | |
opacity: 1; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=100)'); | |
filter: literal('alpha(opacity=100)'); | |
background: #698fad; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.brbpm.lecm.wm.core_widgets.client.panel; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.core.client.Scheduler; | |
import com.google.gwt.dom.client.Document; | |
import com.google.gwt.dom.client.Element; | |
import com.google.gwt.event.dom.client.DomEvent; | |
import com.google.gwt.event.dom.client.HasScrollHandlers; | |
import com.google.gwt.event.dom.client.ScrollEvent; | |
import com.google.gwt.event.dom.client.ScrollHandler; | |
import com.google.gwt.event.shared.HandlerRegistration; | |
import com.google.gwt.uibinder.client.UiBinder; | |
import com.google.gwt.uibinder.client.UiField; | |
import com.google.gwt.user.client.DOM; | |
import com.google.gwt.user.client.Event; | |
import com.google.gwt.user.client.ui.Widget; | |
/** | |
* User: dshahovkin | |
* Date: 28.11.13 | |
* Time: 11:44 | |
*/ | |
public abstract class AbstractScrollbar extends Widget implements HasScrollHandlers { | |
public static final int MINIMAL_KNOB_SIZE = 20; | |
protected static AbstractScrollbarUiBinder uiBinder = GWT.create(AbstractScrollbarUiBinder.class); | |
protected int scrollSize = 0; | |
protected int scrollPosition = 0; | |
@UiField | |
Element knob; | |
private int mouseStartPosition; | |
private boolean scheduled = false; | |
private Scheduler.ScheduledCommand scheduledCommand = new Scheduler.ScheduledCommand() { | |
@Override | |
public void execute() { | |
adjustKnobSize(); | |
redraw(); | |
scheduled = false; | |
} | |
}; | |
private boolean mouseDown = false; | |
private int knobStartPosition; | |
protected AbstractScrollbar() { | |
setElement(uiBinder.createAndBindUi(this)); | |
Event.sinkEvents(knob, Event.ONMOUSEDOWN | Event.ONMOUSEUP | Event.ONMOUSEMOVE | Event | |
.ONSCROLL); | |
Event.sinkEvents(getElement(), Event.ONMOUSEDOWN | Event.ONMOUSEUP | Event.ONMOUSEMOVE | Event | |
.ONSCROLL); | |
} | |
@Override | |
public void onBrowserEvent(Event event) { | |
super.onBrowserEvent(event); | |
switch (event.getTypeInt()) { | |
case Event.ONMOUSEDOWN: | |
event.preventDefault(); | |
if (DOM.eventGetTarget(event).equals(knob)) { | |
Event.setCapture(getElement()); | |
mouseDown = true; | |
mouseStartPosition = getMousePosition(event); | |
knobStartPosition = getKnobPosition(); | |
} else if(DOM.eventGetTarget(event).equals(getElement())) { | |
pageKnob(event); | |
} | |
break; | |
case Event.ONMOUSEUP: | |
if (mouseDown) { | |
mouseDown = false; | |
Event.releaseCapture(getElement()); | |
} | |
event.preventDefault(); | |
break; | |
case Event.ONMOUSEMOVE: | |
if (mouseDown) { | |
int newMousePosition = getMousePosition(event); | |
scrollPosition = getScrollOffset(getPageSize(), getKnobSize(), scrollSize, | |
knobStartPosition - mouseStartPosition + newMousePosition); | |
redraw(); | |
DomEvent.fireNativeEvent(Document.get().createScrollEvent(), this); | |
} | |
break; | |
} | |
} | |
private void pageKnob(Event event) { | |
int position = getAbsoluteKnobPosition(); | |
if (getMousePosition(event) < position) { | |
shiftUp(getPageSize()); | |
} else { | |
shiftDown(getPageSize()); | |
} | |
redraw(); | |
DomEvent.fireNativeEvent(Document.get().createScrollEvent(), this); | |
} | |
private void shiftDown(int pageSize) { | |
scrollPosition += pageSize; | |
} | |
private void shiftUp(int pageSize) { | |
scrollPosition -= pageSize; | |
} | |
protected abstract int getKnobPosition(); | |
protected abstract void setKnobPosition(int knobPosition); | |
protected abstract int getAbsoluteKnobPosition(); | |
protected abstract int getPageSize(); | |
/** | |
* Рассчитывает размер кнопки в скролбаре. | |
*/ | |
private void adjustKnobSize() { | |
if (!isAttached()) { | |
return; | |
} | |
// размер контейнера (высота или ширина) | |
int containerSize = getPageSize(); | |
// если контейнер больше, то кнопка будет по размеру контейнера | |
// fixme кнопка должна быть без бордеров | |
if (containerSize >= scrollSize) { | |
setKnobSize(containerSize); | |
} else { | |
// примерный размер кнопки - пропорционально количеству "страниц" | |
double pageCount = (double) containerSize / scrollSize; | |
int minimalKnobSize = (int) Math.max(MINIMAL_KNOB_SIZE, pageCount * containerSize); | |
// необходимо скрыть кнопку, если ее минимальная высота больше, чем размер контейнера. | |
if (minimalKnobSize > containerSize - 10) { | |
minimalKnobSize = 0; | |
} | |
setKnobSize(minimalKnobSize); | |
} | |
} | |
protected abstract int getKnobSize(); | |
protected abstract void setKnobSize(int height); | |
protected void runCommand() { | |
if (!scheduled) { | |
scheduled = true; | |
Scheduler.get().scheduleDeferred(scheduledCommand); | |
} | |
} | |
public HandlerRegistration addScrollHandler(ScrollHandler handler) { | |
return addHandler(handler, ScrollEvent.getType()); | |
} | |
/** | |
* Перемещение кнопки | |
*/ | |
private void redraw() { | |
// Abort if not attached | |
if (!isAttached()) { | |
return; | |
} | |
int knobOffset = getRealOffset(getPageSize(), getKnobSize(), scrollSize, scrollPosition); | |
setKnobPosition(knobOffset); | |
} | |
private int getRealOffset(int containerSize, int knobSize, int dataSize, int scrollPosition) { | |
// верхняя граница кнопки может двигаться в пределах | |
int viewportDelta = containerSize - knobSize; | |
// верхняя граница видимого контента может двигаться в пределах | |
int realDelta = dataSize - containerSize; | |
// сбросим лишнее | |
int normalizedScrollPosition = Math.max(scrollPosition, 0); | |
normalizedScrollPosition = Math.min(normalizedScrollPosition, realDelta); | |
// пропорция | |
return normalizedScrollPosition * viewportDelta / realDelta; | |
} | |
private int getScrollOffset(int containerSize, int knobSize, int dataSize, int knobPosition) { | |
// верхняя граница кнопки может двигаться в пределах | |
int viewportDelta = containerSize - knobSize; | |
// верхняя граница видимого контента может двигаться в пределах | |
int realDelta = dataSize - containerSize; | |
// сбросим лишнее | |
int normalizedKnobPosition = Math.max(0, knobPosition); | |
normalizedKnobPosition = Math.min(normalizedKnobPosition, viewportDelta); | |
// пропорция | |
return normalizedKnobPosition * realDelta / viewportDelta; | |
} | |
abstract protected int getMousePosition(Event event); | |
interface AbstractScrollbarUiBinder extends UiBinder<Element, AbstractScrollbar> { | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent"> | |
<ui:UiBinder | |
xmlns:ui="urn:ui:com.google.gwt.uibinder"> | |
<ui:style> | |
.viewport { | |
/*position: relative;*/ | |
overflow: hidden; | |
/*background-color: red;*/ | |
} | |
.knob { | |
/*background-color: green;*/ | |
position: absolute; | |
overflow: hidden; | |
} | |
</ui:style> | |
<div class="{style.viewport}"> | |
<div ui:field="knob" class="{style.knob}"/> | |
</div> | |
</ui:UiBinder> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.brbpm.lecm.wm.core_widgets.client.panel; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.dom.client.Style; | |
import com.google.gwt.user.client.Event; | |
import ru.brbpm.lecm.wm.styles.client.widgets.CustomScrollPanelResources; | |
/** | |
* User: dshahovkin | |
* Date: 28.11.13 | |
* Time: 11:52 | |
*/ | |
public class HorizontalScrollbar extends AbstractScrollbar implements com.google.gwt.user.client.ui | |
.HorizontalScrollbar { | |
public HorizontalScrollbar() { | |
CustomScrollPanelResources resources = GWT.create(CustomScrollPanelResources.class); | |
resources.horizontalScrollbar().ensureInjected(); | |
getElement().addClassName(resources.horizontalScrollbar().viewport()); | |
knob.addClassName(resources.horizontalScrollbar().knob()); | |
knob.getStyle().setTop(0, Style.Unit.PX); | |
knob.getStyle().setBottom(0, Style.Unit.PX); | |
knob.getStyle().setWidth(MINIMAL_KNOB_SIZE, Style.Unit.PX); | |
} | |
@Override | |
protected int getKnobPosition() { | |
return knob.getOffsetLeft(); | |
} | |
@Override | |
protected void setKnobPosition(int knobPosition) { | |
knob.getStyle().setLeft(knobPosition, Style.Unit.PX); | |
} | |
@Override | |
protected int getAbsoluteKnobPosition() { | |
return knob.getAbsoluteLeft(); | |
} | |
@Override | |
protected int getPageSize() { | |
return getOffsetWidth(); | |
} | |
@Override | |
protected int getKnobSize() { | |
return knob.getOffsetWidth(); | |
} | |
@Override | |
protected void setKnobSize(int height) { | |
knob.getStyle().setWidth(height, Style.Unit.PX); | |
} | |
@Override | |
protected int getMousePosition(Event event) { | |
return event.getClientX(); | |
} | |
@Override | |
public int getScrollWidth() { | |
return scrollSize; | |
} | |
@Override | |
public void setScrollWidth(int width) { | |
scrollSize = width; | |
runCommand(); | |
} | |
@Override | |
public int getHorizontalScrollPosition() { | |
return scrollPosition; | |
} | |
@Override | |
public void setHorizontalScrollPosition(int position) { | |
scrollPosition = position; | |
runCommand(); | |
} | |
@Override | |
public int getMaximumHorizontalScrollPosition() { | |
return scrollSize; | |
} | |
@Override | |
public int getMinimumHorizontalScrollPosition() { | |
return 0; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.brbpm.lecm.wm.core_widgets.client.panel; | |
import com.google.gwt.dom.client.Element; | |
import com.google.gwt.user.client.ui.CustomScrollPanel; | |
import com.google.gwt.user.client.ui.Widget; | |
/** | |
* User: dshahovkin | |
* Date: 05.12.13 | |
* Time: 17:06 | |
*/ | |
public class LecmCustomScrollPanel extends CustomScrollPanel { | |
public LecmCustomScrollPanel() { | |
initScrollbars(); | |
} | |
public LecmCustomScrollPanel(Resources resources) { | |
super(resources); | |
initScrollbars(); | |
} | |
public LecmCustomScrollPanel(Widget child) { | |
super(child); | |
initScrollbars(); | |
} | |
private void initScrollbars() { | |
setVerticalScrollbar(new VerticalScrollBar(), 10); | |
setHorizontalScrollbar(new HorizontalScrollbar(), 10); | |
} | |
@Override | |
public void setWidget(Widget widget) { | |
super.setWidget(widget); | |
if (widget != null) { | |
Element parent = widget.getElement().getParentElement(); | |
if (parent != null) { | |
/** | |
* LecmCustomScrollPanel applies the inline block style to the container | |
* element, but we want the container to fill the available width. | |
*/ | |
parent.getStyle().setDisplay(com.google.gwt.dom.client.Style.Display.BLOCK); | |
} | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ru.brbpm.lecm.wm.core_widgets.client.panel; | |
import com.google.gwt.core.client.GWT; | |
import com.google.gwt.dom.client.Style; | |
import com.google.gwt.user.client.Event; | |
import ru.brbpm.lecm.wm.styles.client.widgets.CustomScrollPanelResources; | |
/** | |
* User: dshahovkin | |
* Date: 27.11.13 | |
* Time: 10:18 | |
*/ | |
public class VerticalScrollBar extends AbstractScrollbar implements com.google.gwt.user.client.ui.VerticalScrollbar { | |
public VerticalScrollBar() { | |
CustomScrollPanelResources resources = GWT.create(CustomScrollPanelResources.class); | |
resources.verticalScrollbar().ensureInjected(); | |
getElement().addClassName(resources.verticalScrollbar().viewport()); | |
knob.addClassName(resources.verticalScrollbar().knob()); | |
knob.getStyle().setLeft(0, Style.Unit.PX); | |
knob.getStyle().setRight(0, Style.Unit.PX); | |
knob.getStyle().setHeight(MINIMAL_KNOB_SIZE, Style.Unit.PX); | |
} | |
@Override | |
protected int getKnobPosition() { | |
return knob.getOffsetTop(); | |
} | |
@Override | |
protected void setKnobPosition(int knobPosition) { | |
knob.getStyle().setTop(knobPosition, Style.Unit.PX); | |
} | |
@Override | |
protected int getAbsoluteKnobPosition() { | |
return knob.getAbsoluteTop(); | |
} | |
@Override | |
protected int getPageSize() { | |
return getOffsetHeight(); | |
} | |
@Override | |
public int getScrollHeight() { | |
return scrollSize; | |
} | |
@Override | |
public void setScrollHeight(int height) { | |
scrollSize = height; | |
runCommand(); | |
} | |
@Override | |
protected int getKnobSize() { | |
return knob.getOffsetHeight(); | |
} | |
@Override | |
protected void setKnobSize(int height) { | |
knob.getStyle().setHeight(height, Style.Unit.PX); | |
} | |
@Override | |
protected int getMousePosition(Event event) { | |
return event.getClientY(); | |
} | |
@Override | |
public int getMaximumVerticalScrollPosition() { | |
return scrollSize; | |
} | |
@Override | |
public int getMinimumVerticalScrollPosition() { | |
return 0; | |
} | |
@Override | |
public int getVerticalScrollPosition() { | |
return scrollPosition; | |
} | |
@Override | |
public void setVerticalScrollPosition(int position) { | |
scrollPosition = position; | |
runCommand(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.viewport { | |
background: #c8dae8; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
-moz-opacity: 0.7; | |
-khtml-opacity: 0.7; | |
-webkit-opacity: 0.7; | |
opacity: 0.7; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=70)'); | |
filter: literal('alpha(opacity=70)'); | |
} | |
.knob { | |
background: #8aa8bf; | |
-webkit-border-radius: 3px; | |
-moz-border-radius: 3px; | |
border-radius: 3px; | |
-moz-opacity: 0.7; | |
-khtml-opacity: 0.7; | |
-webkit-opacity: 0.7; | |
opacity: 0.7; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=70)'); | |
filter: literal('alpha(opacity=70)'); | |
-webkit-transition: height 600ms ease 0ms; | |
-moz-transition: height 600ms ease 0ms; | |
-o-transition: height 600ms ease 0ms; | |
transition: height 600ms ease 0ms; | |
} | |
.knob:hover { | |
-moz-opacity: 1; | |
-khtml-opacity: 1; | |
-webkit-opacity: 1; | |
opacity: 1; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=100)'); | |
filter: literal('alpha(opacity=100)'); | |
background: #7a9cb6; | |
} | |
.knob:active { | |
-moz-opacity: 1; | |
-khtml-opacity: 1; | |
-webkit-opacity: 1; | |
opacity: 1; | |
-ms-filter: literal('progid:DXImageTransform.Microsoft.Alpha(opacity=100)'); | |
filter: literal('alpha(opacity=100)'); | |
background: #698fad; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment