Skip to content

Instantly share code, notes, and snippets.

@DemkaAge
Last active August 29, 2015 13:56
Show Gist options
  • Save DemkaAge/9343436 to your computer and use it in GitHub Desktop.
Save DemkaAge/9343436 to your computer and use it in GitHub Desktop.
Custom Scroll Bar
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();
}
.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;
}
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> {
}
}
<!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>
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;
}
}
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);
}
}
}
}
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();
}
}
.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