435 lines
11 KiB
ActionScript
435 lines
11 KiB
ActionScript
import elements.BaseGuiElement;
|
|
import elements.GuiScrollbar;
|
|
|
|
export GuiPanel, ScrollType;
|
|
|
|
enum ScrollType {
|
|
ST_Always,
|
|
ST_Never,
|
|
ST_Auto,
|
|
};
|
|
|
|
class GuiPanel : BaseGuiElement {
|
|
recti RealAbsolutePosition;
|
|
vec2i virtualSize;
|
|
vec2d scrollOffset;
|
|
vec2i Origin;
|
|
recti minPanelSize;
|
|
recti sizePadding;
|
|
|
|
ScrollType horizType;
|
|
ScrollType vertType;
|
|
|
|
GuiScrollbar@ horiz;
|
|
GuiScrollbar@ vert;
|
|
|
|
bool ScrollPane = false;
|
|
bool ShowPaneScroll = false;
|
|
int dragThreshold = 5;
|
|
bool allowScrollDrag = true;
|
|
|
|
vec2i dragStart;
|
|
bool heldLeft = false;
|
|
bool isDragging = false;
|
|
bool animating = false;
|
|
bool LeftDrag = true;
|
|
bool MiddleDrag = true;
|
|
|
|
GuiPanel(IGuiElement@ Parent, const recti& Rectangle) {
|
|
super(Parent, Rectangle);
|
|
_GuiPanel();
|
|
}
|
|
|
|
GuiPanel(IGuiElement@ Parent, Alignment@ Align) {
|
|
super(Parent, Align);
|
|
_GuiPanel();
|
|
}
|
|
|
|
void _GuiPanel() {
|
|
@horiz = GuiScrollbar(null, recti());
|
|
horiz.orientation = SO_Horizontal;
|
|
horiz.scroll = 40;
|
|
horiz.alignment.left.set(AS_Left, 0.0, 0);
|
|
horiz.alignment.right.set(AS_Right, 0.0, 0);
|
|
horiz.alignment.top.set(AS_Bottom, 0.0, 0);
|
|
horiz.alignment.bottom.set(AS_Bottom, 0.0, -20);
|
|
|
|
@vert = GuiScrollbar(null, recti());
|
|
vert.scroll = 40;
|
|
vert.alignment.left.set(AS_Right, 0.0, 0);
|
|
vert.alignment.right.set(AS_Right, 0.0, -20);
|
|
vert.alignment.top.set(AS_Top, 0.0, 0);
|
|
vert.alignment.bottom.set(AS_Bottom, 0.0, 0);
|
|
|
|
horizType = ST_Auto;
|
|
vertType = ST_Auto;
|
|
|
|
@horiz.parent = this;
|
|
@vert.parent = this;
|
|
}
|
|
|
|
void addChild(IGuiElement@ ele) {
|
|
BaseGuiElement::addChild(ele);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void removeChild(IGuiElement@ ele) {
|
|
BaseGuiElement::removeChild(ele);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void scrollToVert(int position) {
|
|
vert.pos = clamp(position, vert.start, vert.end-vert.page);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void setScrollPane(bool value, bool showScrollbars = false) {
|
|
ShowPaneScroll = showScrollbars;
|
|
if(ScrollPane == value)
|
|
return;
|
|
ScrollPane = value;
|
|
scrollOffset = vec2d();
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void center() {
|
|
updateAbsolutePosition();
|
|
scrollOffset = (vec2d(virtualSize) - vec2d(size)) / 2 + vec2d(Origin);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void centerAround(const vec2i& pos) {
|
|
updateAbsolutePosition();
|
|
scrollOffset = vec2d(pos) - vec2d(size) / 2;
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void stopDrag() {
|
|
heldLeft = false;
|
|
isDragging = false;
|
|
dragStart = vec2i();
|
|
}
|
|
|
|
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
|
|
if(ScrollPane) {
|
|
if(source is this || isAncestorOf(source)) {
|
|
switch(event.type) {
|
|
case MET_Button_Down:
|
|
if((LeftDrag && event.button == 0) || (MiddleDrag && event.button == 2)) {
|
|
heldLeft = true;
|
|
dragStart = mousePos;
|
|
return true;
|
|
}
|
|
break;
|
|
case MET_Moved: {
|
|
vec2i d = mousePos - dragStart;
|
|
|
|
if(isDragging) {
|
|
scroll(d.x, d.y);
|
|
dragStart = mousePos;
|
|
}
|
|
else if(heldLeft) {
|
|
if(abs(d.x) >= dragThreshold || abs(d.y) >= dragThreshold) {
|
|
isDragging = true;
|
|
scroll(d.x, d.y);
|
|
dragStart = mousePos;
|
|
}
|
|
}
|
|
} break;
|
|
case MET_Button_Up:
|
|
if((LeftDrag && event.button == 0) || (MiddleDrag && event.button == 2)) {
|
|
heldLeft = false;
|
|
dragStart = vec2i();
|
|
if(isDragging) {
|
|
isDragging = false;
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if(event.type == MET_Scrolled && allowScrollDrag) {
|
|
if(virtualSize.height > size.height) {
|
|
if(ctrlKey && virtualSize.width > size.width)
|
|
scroll(event.y * 30, 0);
|
|
else
|
|
scroll(0, event.y * 30);
|
|
return true;
|
|
}
|
|
else if(virtualSize.width > size.width) {
|
|
scroll(event.y * 30, 0);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if(event.type == MET_Scrolled) {
|
|
if(horiz.visible && (horiz.AbsolutePosition.isWithin(mousePos) || !vert.visible))
|
|
return horiz.onMouseEvent(event, horiz);
|
|
else if(vert.visible)
|
|
return vert.onMouseEvent(event, vert);
|
|
}
|
|
}
|
|
return BaseGuiElement::onMouseEvent(event, source);
|
|
}
|
|
|
|
bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) {
|
|
if(source !is horiz && source !is vert) {
|
|
switch(event.key) {
|
|
case KEY_LEFT:
|
|
case KEY_RIGHT:
|
|
if(horiz.visible)
|
|
return horiz.onKeyEvent(event, horiz);
|
|
break;
|
|
case KEY_UP:
|
|
case KEY_DOWN:
|
|
case KEY_PAGEUP:
|
|
case KEY_PAGEDOWN:
|
|
if(vert.visible)
|
|
return vert.onKeyEvent(event, vert);
|
|
break;
|
|
}
|
|
}
|
|
return BaseGuiElement::onKeyEvent(event, source);
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& event) {
|
|
if(event.type == GUI_Changed) {
|
|
if(event.caller is vert || event.caller is horiz) {
|
|
updateAbsolutePosition();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return BaseGuiElement::onGuiEvent(event);
|
|
}
|
|
|
|
bool get_onScreen() const {
|
|
return RealAbsolutePosition.overlaps(recti(vec2i(), screenSize));
|
|
}
|
|
|
|
void scroll(double dx, double dy) {
|
|
vec2i delta = vec2i(scrollOffset);
|
|
scrollOffset -= vec2d(dx, dy);
|
|
scrollOffset.x = clamp(scrollOffset.x, double(Origin.x),
|
|
max(double(virtualSize.width - size.width + Origin.x), double(Origin.x)));
|
|
scrollOffset.y = clamp(scrollOffset.y, double(Origin.y),
|
|
max(double(virtualSize.height - size.height + Origin.y), double(Origin.y)));
|
|
|
|
delta -= vec2i(scrollOffset);
|
|
|
|
if(delta.x != 0 || delta.y != 0) {
|
|
AbsolutePosition += delta;
|
|
for(uint i = 0, cnt = Children.length; i < cnt; ++i) {
|
|
auto@ chld = Children[i];
|
|
if(chld is vert || chld is horiz)
|
|
continue;
|
|
chld.abs_move(delta);
|
|
}
|
|
}
|
|
|
|
if(ShowPaneScroll) {
|
|
horiz.pos = scrollOffset.x - Origin.x;
|
|
vert.pos = scrollOffset.y - Origin.y;
|
|
}
|
|
}
|
|
|
|
void setAnimating(bool value) {
|
|
animating = value;
|
|
}
|
|
|
|
void updateAbsolutePosition() {
|
|
if(horiz is null || vert is null)
|
|
return;
|
|
//Resize actual panel
|
|
if(Parent !is null) {
|
|
if(Alignment !is null) {
|
|
Position = Alignment.resolve(Parent.absolutePosition.size);
|
|
ClipRect = recti(vec2i(), Position.size);
|
|
}
|
|
|
|
AbsolutePosition = Position + Parent.absolutePosition.topLeft;
|
|
AbsoluteClipRect = (ClipRect + AbsolutePosition.topLeft).clipAgainst(Parent.absoluteClipRect);
|
|
}
|
|
else {
|
|
if(Alignment !is null) {
|
|
Position = Alignment.resolve(screenSize);
|
|
ClipRect = recti(vec2i(), Position.size);
|
|
}
|
|
|
|
AbsolutePosition = Position;
|
|
AbsoluteClipRect = ClipRect + Position.topLeft;
|
|
}
|
|
|
|
//Figure out virtual size of canvas
|
|
uint cCnt = Children.length();
|
|
Origin = AbsolutePosition.topLeft + minPanelSize.topLeft;
|
|
vec2i realSize = AbsolutePosition.size;
|
|
virtualSize = minPanelSize.size;
|
|
|
|
if(vert.visible)
|
|
AbsolutePosition.botRight.x -= 20;
|
|
if(horiz.visible)
|
|
AbsolutePosition.botRight.y -= 20;
|
|
|
|
for(uint i = 0; i != cCnt; ++i) {
|
|
IGuiElement@ child = Children[i];
|
|
if(child is horiz || child is vert || !child.visible)
|
|
continue;
|
|
|
|
child.updateAbsolutePosition();
|
|
vec2i topLeft = child.absolutePosition.topLeft;
|
|
if(topLeft.x < Origin.x)
|
|
Origin.x = topLeft.x;
|
|
if(topLeft.y < Origin.y)
|
|
Origin.y = topLeft.y;
|
|
}
|
|
|
|
for(uint i = 0; i != cCnt; ++i) {
|
|
IGuiElement@ child = Children[i];
|
|
if(child is horiz || child is vert || !child.visible)
|
|
continue;
|
|
|
|
vec2i botRight = child.absolutePosition.botRight;
|
|
virtualSize.width = max(virtualSize.width, botRight.x - Origin.x);
|
|
virtualSize.height = max(virtualSize.height, botRight.y - Origin.y);
|
|
}
|
|
|
|
Origin -= sizePadding.topLeft;
|
|
virtualSize += sizePadding.topLeft;
|
|
virtualSize += sizePadding.botRight;
|
|
|
|
//Update scrollbars
|
|
Origin -= AbsolutePosition.topLeft;
|
|
if(!ScrollPane || ShowPaneScroll) {
|
|
horiz.end = virtualSize.width;
|
|
horiz.page = AbsolutePosition.width;
|
|
horiz.bar = horiz.page;
|
|
horiz.pos = max(horiz.start, min(horiz.end-horiz.page, horiz.pos));
|
|
|
|
vert.end = virtualSize.height;
|
|
vert.page = AbsolutePosition.height;
|
|
vert.bar = vert.page;
|
|
vert.pos = max(vert.start, min(vert.end-vert.page, vert.pos));
|
|
|
|
scrollOffset = vec2d(horiz.pos + Origin.x, vert.pos + Origin.y);
|
|
|
|
//Check which scrollbars should be visible
|
|
bool prevHoriz = horiz.visible;
|
|
bool prevVert = vert.visible;
|
|
|
|
if(animating) {
|
|
vert.visible = false;
|
|
horiz.visible = false;
|
|
}
|
|
else {
|
|
vert.visible = (virtualSize.height > realSize.height && vertType != ST_Never) || vertType == ST_Always;
|
|
horiz.visible = (virtualSize.width > realSize.width && horizType != ST_Never) || horizType == ST_Always;
|
|
if(horiz.visible && !vert.visible)
|
|
vert.visible = (virtualSize.height > realSize.height - 20 && vertType != ST_Never) || vertType == ST_Always;
|
|
}
|
|
|
|
if(prevHoriz != horiz.visible || prevVert != vert.visible) {
|
|
updateAbsolutePosition();
|
|
return;
|
|
}
|
|
|
|
//Update scrollbar positions
|
|
horiz.updateAbsolutePosition();
|
|
vert.updateAbsolutePosition();
|
|
}
|
|
else {
|
|
scroll(0, 0);
|
|
horiz.visible = false;
|
|
vert.visible = false;
|
|
}
|
|
|
|
//Fake absolute position and reposition children
|
|
RealAbsolutePosition = AbsolutePosition;
|
|
AbsolutePosition -= vec2i(scrollOffset);
|
|
|
|
if(horiz.visible)
|
|
AbsoluteClipRect.botRight.y -= 20;
|
|
if(vert.visible)
|
|
AbsoluteClipRect.botRight.x -= 20;
|
|
|
|
for(uint i = 0; i != cCnt; ++i) {
|
|
if(Children[i] is horiz || Children[i] is vert)
|
|
continue;
|
|
Children[i].updateAbsolutePosition();
|
|
}
|
|
}
|
|
|
|
void move(const vec2i& moveBy) {
|
|
Position += moveBy;
|
|
AbsolutePosition += moveBy;
|
|
RealAbsolutePosition += moveBy;
|
|
AbsoluteClipRect = ClipRect + RealAbsolutePosition.topLeft;
|
|
if(Parent !is null)
|
|
AbsoluteClipRect = AbsoluteClipRect.clipAgainst(Parent.absoluteClipRect);
|
|
|
|
uint cCnt = Children.length();
|
|
for(uint i = 0; i != cCnt; ++i)
|
|
Children[i].abs_move(moveBy);
|
|
}
|
|
|
|
void abs_move(const vec2i& moveBy) {
|
|
AbsolutePosition += moveBy;
|
|
RealAbsolutePosition += moveBy;
|
|
AbsoluteClipRect = ClipRect + RealAbsolutePosition.topLeft;
|
|
if(Parent !is null)
|
|
AbsoluteClipRect = AbsoluteClipRect.clipAgainst(Parent.absoluteClipRect);
|
|
|
|
uint cCnt = Children.length();
|
|
for(uint i = 0; i != cCnt; ++i)
|
|
Children[i].abs_move(moveBy);
|
|
}
|
|
|
|
void set_horizPosition(int pos) {
|
|
horiz.pos = min(double(pos), horiz.end - horiz.page);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void set_vertPosition(int pos) {
|
|
vert.pos = min(double(pos), vert.end - vert.page);
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
recti get_absolutePosition() {
|
|
return RealAbsolutePosition;
|
|
}
|
|
|
|
void draw() {
|
|
if(!visible)
|
|
return;
|
|
|
|
clearClip();
|
|
if(horiz.visible)
|
|
horiz.draw();
|
|
|
|
if(vert.visible)
|
|
vert.draw();
|
|
|
|
uint cCnt = Children.length();
|
|
for(uint i = 0; i != cCnt; ++i) {
|
|
if(Children[i] is horiz || Children[i] is vert)
|
|
continue;
|
|
|
|
IGuiElement@ ele = Children[i];
|
|
|
|
if(!ele.visible || !RealAbsolutePosition.overlaps(ele.absolutePosition))
|
|
continue;
|
|
|
|
if(ele.noClip)
|
|
clearClip();
|
|
else
|
|
setClip(ele.absoluteClipRect);
|
|
|
|
ele.draw();
|
|
|
|
if(!ele.noClip)
|
|
clearClip();
|
|
}
|
|
}
|
|
};
|