319 lines
11 KiB
D
319 lines
11 KiB
D
/* MinWin Scroll classes
|
|
*
|
|
* ScrollBar and ScrollPane
|
|
*
|
|
* Written by Ben Hinkle and released to the public domain, as
|
|
* explained at http://creativecommons.org/licenses/publicdomain
|
|
* Report comments and bugs at dsource: http://www.dsource.org/projects/minwin
|
|
*/
|
|
|
|
module minwin.scroll;
|
|
|
|
public import minwin.component;
|
|
|
|
private {
|
|
import minwin.app;
|
|
import minwin.window;
|
|
import minwin.peerimpl;
|
|
import std.string;
|
|
import minwin.logging;
|
|
}
|
|
|
|
version (GTK) version = SharedScrollData;
|
|
version (SharedScrollData) {
|
|
struct ScrollData {
|
|
int min;
|
|
int max;
|
|
int increment;
|
|
int value;
|
|
}
|
|
}
|
|
enum ScrollMask {
|
|
Range = 1, Increment = 2, Value = 4
|
|
}
|
|
const int Horizontal = 1;
|
|
const int Vertical = 2;
|
|
|
|
version (MinWin32) {
|
|
|
|
private import minwin.mswindows;
|
|
|
|
alias SCROLLINFO ScrollData;
|
|
|
|
class ScrollBar : WindowChild {
|
|
int orientation;
|
|
this(Component parent, int orientation, char[] name = "") {
|
|
PeerForAdd parentp = parent.getPeerForAdd();
|
|
uint style = orientation==Horizontal?SBS_HORZ:SBS_VERT;
|
|
peer = CreateWindowA("SCROLLBAR","",
|
|
style | WS_CHILD | WS_VISIBLE,
|
|
0,0,10,10,parentp,
|
|
cast(HMENU)0,gApp.hInstance,null);
|
|
sysAssert(peer !is null, "Failed to create peer scrollbar");
|
|
this.name = name;
|
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
|
parent.addChild(this);
|
|
}
|
|
Point preferredSize() {
|
|
int width,height;
|
|
if (orientation == Vertical) {
|
|
height = 50;
|
|
width = 16;
|
|
} else {
|
|
height = 16;
|
|
width = 50;
|
|
}
|
|
if (userPreferredWidth > 0)
|
|
width = userPreferredWidth;
|
|
if (userPreferredHeight > 0)
|
|
height = userPreferredHeight;
|
|
return XY(width,height);
|
|
}
|
|
mixin WindowChildImpl!();
|
|
|
|
private uint windowsMask(uint mask) {
|
|
uint res = 0;
|
|
if (mask & ScrollMask.Range)
|
|
res |= SIF_RANGE;
|
|
if (mask & ScrollMask.Value)
|
|
res |= SIF_POS;
|
|
if (mask & ScrollMask.Increment)
|
|
res |= SIF_PAGE;
|
|
return res;
|
|
}
|
|
|
|
void setData(inout ScrollData state, uint mask) {
|
|
state.cbSize = SCROLLINFO.sizeof;
|
|
state.fMask = windowsMask(mask);
|
|
SetScrollInfo(peer,SB_CTL,&state, false);
|
|
}
|
|
void getData(inout ScrollData state, uint mask) {
|
|
state.cbSize = SCROLLINFO.sizeof;
|
|
state.fMask = windowsMask(mask);
|
|
GetScrollInfo(peer,SB_CTL,&state);
|
|
}
|
|
int value() {
|
|
ScrollData state;
|
|
getData(state,ScrollMask.Value);
|
|
return state.nPos;
|
|
}
|
|
}
|
|
|
|
class ScrollPane : WindowChild {
|
|
Point origin;
|
|
|
|
// how to customize page size and steps and limits? need API to each scrollbar
|
|
|
|
int hstep = 1; // given bounds of child this is how quickly to move through it
|
|
int vstep = 1;
|
|
|
|
this(Component parent, int orientations = Horizontal | Vertical, char[] name = "") {
|
|
PeerForAdd parentp = parent.getPeerForAdd();
|
|
int style;
|
|
if (orientations & Horizontal) style |= WS_HSCROLL;
|
|
if (orientations & Vertical) style |= WS_VSCROLL;
|
|
peer = CreateWindowA("MinWinScrollWindow","",
|
|
style | WS_CHILD | WS_VISIBLE,
|
|
0,0,50,50,parentp,
|
|
cast(HMENU)0,gApp.hInstance,null);
|
|
this.name = name;
|
|
clearBackgroundOnPaint = false;
|
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
|
parent.addChild(this);
|
|
}
|
|
mixin WindowChildImpl!();
|
|
|
|
void layout(bool validateParent) {
|
|
if (parent && validateParent)
|
|
parent.layout(true);
|
|
if (childLayoutDirty && child) {
|
|
Point s = child.preferredSize;
|
|
Rect r = LTWH(origin.x,origin.y,s.x,s.y);
|
|
child.setBounds(r);
|
|
childLayoutDirty = false;
|
|
}
|
|
foreach( Component ch; this) {
|
|
ch.layout(false);
|
|
}
|
|
}
|
|
|
|
int WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) {
|
|
version (LOG) log.printf(" in ScrollPane windowproc msg %d\n",uMsg);
|
|
bool doDefault = true;
|
|
if (uMsg == WM_HSCROLL) {
|
|
SCROLLINFO data;
|
|
data.cbSize = SCROLLINFO.sizeof;
|
|
data.fMask = SIF_ALL;
|
|
GetScrollInfo(hWnd, SB_HORZ, &data);
|
|
int cmd = LOWORD(wParam);
|
|
int oldPos = data.nPos;
|
|
if (cmd == SB_LINELEFT) data.nPos--;
|
|
if (cmd == SB_LINERIGHT) data.nPos++;
|
|
if (cmd == SB_PAGELEFT) data.nPos -= data.nPage;
|
|
if (cmd == SB_PAGERIGHT) data.nPos += data.nPage;
|
|
data.fMask = SIF_POS;
|
|
SetScrollInfo(hWnd, SB_HORZ, &data, true);
|
|
GetScrollInfo(hWnd, SB_HORZ, &data);
|
|
if (data.nPos != oldPos) {
|
|
int step = hstep*(oldPos-data.nPos);
|
|
ScrollWindowEx(hWnd,step,0,null,null,null,null,SW_INVALIDATE | SW_ERASE);
|
|
// ScrollWindow(hWnd,step,0,null,null);
|
|
if (child) {
|
|
WindowChild wc = cast(WindowChild)child;
|
|
MoveWindow(wc.getPeer,step,0,0,0,false);
|
|
UpdateWindow(wc.getPeer);
|
|
}
|
|
origin.XY(origin.x + step, origin.y);
|
|
}
|
|
doDefault = false;
|
|
} else if (uMsg == WM_VSCROLL) {
|
|
SCROLLINFO data;
|
|
data.cbSize = SCROLLINFO.sizeof;
|
|
data.fMask = SIF_ALL;
|
|
GetScrollInfo(hWnd, SB_VERT, &data);
|
|
int cmd = LOWORD(wParam);
|
|
int oldPos = data.nPos;
|
|
if (cmd == SB_LINEUP) data.nPos--;
|
|
if (cmd == SB_LINEDOWN) data.nPos++;
|
|
if (cmd == SB_PAGEUP) data.nPos -= data.nPage;
|
|
if (cmd == SB_PAGEDOWN) data.nPos += data.nPage;
|
|
data.fMask = SIF_POS;
|
|
SetScrollInfo(hWnd, SB_VERT, &data, true);
|
|
GetScrollInfo(hWnd, SB_VERT, &data);
|
|
if (data.nPos != oldPos) {
|
|
int step = vstep*(oldPos-data.nPos);
|
|
ScrollWindowEx(hWnd,0,step,null,null,null,null,SW_INVALIDATE | SW_ERASE);
|
|
// ScrollWindow(hWnd,0,step,null,null);
|
|
if (child) {
|
|
WindowChild wc = cast(WindowChild)child;
|
|
MoveWindow(wc.getPeer,0,step,0,0,false);
|
|
UpdateWindow(wc.getPeer);
|
|
}
|
|
origin.XY(origin.x, origin.y + step);
|
|
}
|
|
doDefault = false;
|
|
}
|
|
return doDefault;
|
|
}
|
|
}
|
|
|
|
extern(Windows) int MinWinScrollWindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) {
|
|
version(LOG) log.printf("in minwin scroll window proc hwnd %p %d %d %d\n",hWnd,uMsg,wParam,lParam);
|
|
bool doDefault = true;
|
|
ScrollPane pane = cast(ScrollPane)peerToWindowChild(hWnd);
|
|
version(LOG) log.printf("in minwin scroll window proc win %p\n",pane);
|
|
if (pane !is null)
|
|
doDefault = pane.WindowProc(hWnd,uMsg,wParam,lParam) != 0;
|
|
if (doDefault)
|
|
return DefWindowProcA(hWnd, uMsg, wParam, lParam);
|
|
else
|
|
return 0;
|
|
}
|
|
static this() {
|
|
HINSTANCE hInst = GetModuleHandleA(null);
|
|
WNDCLASSA wc;
|
|
wc.lpszClassName = "MinWinScrollWindow";
|
|
wc.style = CS_CLASSDC | CS_HREDRAW | CS_VREDRAW;
|
|
wc.lpfnWndProc = &MinWinScrollWindowProc;
|
|
wc.hInstance = hInst;
|
|
wc.hIcon = LoadIconA(cast(HINSTANCE) null, IDI_APPLICATION);
|
|
// wc.hIconSm = DefaultWindowSmallIcon.peer;
|
|
wc.hCursor = LoadCursorA(cast(HINSTANCE) null, IDC_ARROW);
|
|
wc.hbrBackground = cast(HBRUSH) (COLOR_WINDOW + 1); // not +1 for default
|
|
wc.lpszMenuName = null;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
RegisterClassA(&wc);
|
|
}
|
|
|
|
} else version (GTK) {
|
|
|
|
private import minwin.gtk;
|
|
private import minwin.gtk_peers;
|
|
|
|
class ScrollBar : WindowChild {
|
|
this(Component parent, int orientation, char[] name = "") {
|
|
PeerForAdd parentp = parent.getPeerForAdd();
|
|
this.name = name;
|
|
if (orientation == Horizontal)
|
|
peer = gtk_hscrollbar_new(null);
|
|
else
|
|
peer = gtk_vscrollbar_new(null);
|
|
gtk_container_add(cast(GtkContainer*)parentp,peer);
|
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
|
parent.addChild(this);
|
|
gtk_widget_realize(peer);
|
|
visible = true;
|
|
}
|
|
|
|
mixin WindowChildImpl!();
|
|
|
|
}
|
|
|
|
class ScrollPane : WindowChild {
|
|
MinWinGtkPeer* content;
|
|
this(Component parent, int orientations = Horizontal | Vertical, char[] name = "") {
|
|
PeerForAdd parentp = parent.getPeerForAdd();
|
|
this.name = name;
|
|
peer = gtk_scrolled_window_new(null,null);
|
|
gtk_container_add(cast(GtkContainer*)parentp,peer);
|
|
GtkWidget* view = gtk_viewport_new(null,null);
|
|
gtk_container_add(cast(GtkContainer*)peer,view);
|
|
|
|
// add our peer to hook into GTK size allocation algorithm
|
|
GtkWidget* wcontent = MinWinGtkPeer_new();
|
|
content = cast(MinWinGtkPeer*)wcontent;
|
|
gtk_widget_set_sensitive(wcontent,true);
|
|
content.sizeRequest = >kRequest;
|
|
content.sizeAllocate = >kAllocate;
|
|
gtk_container_add(cast(GtkContainer*)view,wcontent);
|
|
|
|
setWindowChildPeer(this,peer,OWNS_PEER);
|
|
parent.addChild(this);
|
|
|
|
// are all of these really needed?
|
|
gtk_widget_realize(wcontent);
|
|
gtk_widget_show(wcontent);
|
|
gtk_widget_realize(view);
|
|
gtk_widget_show(view);
|
|
gtk_widget_realize(peer);
|
|
visible = true;
|
|
}
|
|
|
|
mixin WindowChildImpl!();
|
|
|
|
void layout(bool validateParent) {
|
|
if (parent && validateParent)
|
|
parent.layout(true);
|
|
childLayoutDirty = false;
|
|
}
|
|
|
|
PeerForAdd getPeerForAdd() {
|
|
return cast(GtkWidget*)content;
|
|
}
|
|
|
|
// callback from the GTK peer asking for preferred size
|
|
private void gtkRequest(GtkWidget *w, GtkRequisition* req) {
|
|
version(LOG)log.writefln("gtkRequest for scrollpane");
|
|
Component ch = child;
|
|
if (ch is null) return;
|
|
Point s = ch.preferredSize;
|
|
req.width = s.x;
|
|
req.height = s.y;
|
|
version(LOG)log.writefln("done gtkRequest for scrollpane");
|
|
}
|
|
|
|
// callback from GTK peer telling us our position
|
|
private void gtkAllocate(GtkWidget *w, GtkAllocation* req) {
|
|
version(LOG)log.writefln("gtkAllocate for scrollpane");
|
|
Component ch = child;
|
|
if (ch is null) return;
|
|
Rect r = toRect(*req);
|
|
ch.size = XY(r.width,r.height);
|
|
ch.layout(false);
|
|
childLayoutDirty = false;
|
|
version(LOG)log.writefln("done gtkAllocate for scrollpane");
|
|
}
|
|
|
|
}
|
|
}
|