Insert dragable and resizeable subgraphs into a graph window

This code provides a hook function which makes it possible to drag and resize subwindows in a graph with the mouse. To see how this works create a graph and execute the following:

TestAppendSubGraph(mainGraphName, plotThisWave)

This will insert a subpanel with a subgraph into the designated graph window, which can then be interacted with via the mouse. You can add as many subgraphs as you want. The subwindows are also prevented from disappearing outside the main graph area upon resizing the main graph. A minimum subwindow size is set by the subMinSize constant. The detection range for subwindow edge (i.e., where the dragging mode switches to resizing close to window edges) is controlled via the edgeDetect constant.

If you want to remove the hook and associated user data again, use:

TestRemoveHook(GraphName)

Here is the full code:

#pragma TextEncoding = "UTF-8"
#pragma rtGlobals=3
#pragma DefaultTab={3,20,4}

function TestAppendSubGraph(string mainGName, wave plotThis)
	GetWindow $mainGName, expand
	variable gExpand = V_value;	gExpand = gExpand == 0 ? 1 : gExpand	// pixel conversion and expansion factor
	variable pixConv = ScreenResolution/72*gExpand
	
	GetWindow $mainGName psizeDC		// define an initial size
	variable size = max(min((V_bottom-V_top)/4,(V_right-V_left)/5),subMinSize)/pixConv
	variable wL = (V_left+20)/pixConv, wT = (V_top+20)/pixConv, wR = wL+size*1.2, wB = wT+size
	
	NewPanel/HOST=$mainGName/N=floatPanel/W=(wL,wT,wR,wB)
	Display/HOST=#/FG=(FL,FT,FR,FB) plotThis
	ModifyGraph frameStyle=1		// edges are easier to see with a frame
	SetActiveSubwindow $mainGName
	SetWindow $mainGName hook(DragUpdate)=DragHookFunction
end

function TestRemoveHook(string gName)
	string subWinList = ChildWindowList(gName)
	int i
	for (i=0; i<ItemsInList(subWinList); i++)
		SetWindow $gName userdata($StringFromList(i,subWinList))=""		// remove user data from graph
	endfor
	SetWindow $gName userdata(dragInfo)=""
	SetWindow $gName hook(DragUpdate)=$""
end

// ##################

static Constant subMinSize = 100
static Constant edgeDetect = 15

function DragHookFunction(STRUCT WMWinHookStruct &s)
	string gName = StringFromList(0,s.winName,"#")
	string sName = StringFromList(1,s.winName,"#")
	string dragInfo = "", floatStruct = ""
	int i, hookTakeOver = 0
	
	GetWindow $gName, expand
	variable gExpand = V_value;	gExpand = gExpand == 0 ? 1 : gExpand	// pixel conversion and expansion factor
	variable pixConv = ScreenResolution/72*gExpand
	variable winEdge = 0, dragEdge = 0
	STRUCT DragStruct d
	STRUCT rect tempRect
	
	if (WhichListItem(s.eventName, "mousedown;mousemoved;mouseup;")>-1 && CmpStr(s.winName, gName))		// mouse events on the sub-graph
		GetWindow $s.winName psizeDC
		tempRect.top    = v_top
		tempRect.left   = v_left
		tempRect.right  = v_right
		tempRect.bottom = v_bottom
		if (!CheckPointInRect(s.mouseLoc,tempRect))		// mouse is not in plot area
			GetWindow $s.winName gsizeDC;	 winEdge = 1			// encode sub-window mouse edge detection into winEdge
			winEdge = abs(s.mouseloc.h-v_left) <edgeDetect && abs(s.mouseloc.v-v_top)   <edgeDetect ? 2 : winEdge	// LT
			winEdge = abs(s.mouseloc.h-v_right)<edgeDetect && abs(s.mouseloc.v-v_top)   <edgeDetect ? 3 : winEdge	// RT
			winEdge = abs(s.mouseloc.h-v_left) <edgeDetect && abs(s.mouseloc.v-v_bottom)<edgeDetect ? 4 : winEdge	// LB
			winEdge = abs(s.mouseloc.h-v_right)<edgeDetect && abs(s.mouseloc.v-v_bottom)<edgeDetect ? 5 : winEdge	// RB
		endif
	endif
	
	StrSwitch(s.eventName)
		case "mousedown":
			if (CmpStr(s.winName, gName) && s.eventMod==1)
				if (CheckPointInRect(s.mouseLoc,tempRect))
					break
				endif
				GetWindow $gName gsizeDC
				d.graphRect.top    = v_top
				d.graphRect.left   = v_left
				d.graphRect.bottom = v_bottom
				d.graphRect.right  = v_right
				d.mouse_init = s.mouseloc
				d.subwinRect = s.winrect
				StructPut/S d, floatStruct
				SetWindow $gName userdata($sName)=floatStruct			// save info for the current subwindow
				SetWindow $gName userdata(dragInfo)=num2str(winEdge)+";"+sName+";"		// a list of the edge to drag and the dragged subwindow name
				hookTakeOver = 1
			endif
		break
		case "mouseup":
			if (str2num(GetUserData(gName, "", "dragInfo")))
				SetActiveSubwindow $gName
				SetWindow $gName userdata(dragInfo)=""
			endif
		break
		case "mousemoved":
			if (winEdge)
				s.cursorCode = str2num(StringFromList(winEdge,"0;8;4;11;10;7;"))
				s.doSetCursor = 1	// change mouse cursor
			endif
			dragInfo = GetUserData(gName, "", "dragInfo")
			dragEdge = str2num(StringFromList(0,dragInfo))
			dragEdge = numtype(dragEdge) != 0 ? 0 : dragEdge
			if (dragEdge)
                GetMouse/W=$gName
                if (!(V_flag & 1))	// mouse button released? => maybe we missed a mouse-up event?
                    SetWindow $gName userdata(dragInfo)=""
					dragEdge = 0
                endif
			else
				break
            endif
			
			sName = StringFromList(1,dragInfo)			
			StructGet/S d, GetUserData(gName, "", sName)
			variable deltah = s.mouseloc.h - d.mouse_init.h
			variable deltav = s.mouseloc.v - d.mouse_init.v
			tempRect = d.subwinRect
			tempRect.top    += deltav * (dragEdge == 1 || dragEdge == 2 || dragEdge == 3)
			tempRect.left   += deltah * (dragEdge == 1 || dragEdge == 2 || dragEdge == 4)
			tempRect.right  += deltah * (dragEdge == 1 || dragEdge == 5 || dragEdge == 3)
			tempRect.bottom += deltav * (dragEdge == 1 || dragEdge == 5 || dragEdge == 4)
			
			string subWin = gName + "#" + sName
			if (CheckRectPosAndSize(tempRect, d.graphRect) && WinType(subWin))
				MoveSubwindow/W=$subWin fnum=(tempRect.left/pixConv, tempRect.top/pixConv, tempRect.right/pixConv, tempRect.bottom/pixConv)
				d.subwinRect = tempRect
			endif
			d.mouse_init = s.mouseloc
			StructPut/S d, floatStruct
			SetWindow $gName userdata($sName)=floatStruct
		break
		case "resize":
			string subWinList = ChildWindowList(gName)
			if (!ItemsInList(subWinList))
				break
			endif
			
			GetWindow $gName wsizeDC
			int isHostMinW = abs(v_right-v_left) <= subMinSize
			int isHostMinH = abs(v_bottom-v_top) <= subMinSize
			for (i=0; i<ItemsInList(subWinList); i++)
				sName = StringFromList(i, subWinList)
				GetWindow $(gName+"#"+sName) wsizeDC
				variable subW = abs(v_right-v_left)
				variable subH = abs(v_bottom-v_top)
				
				variable dW = isHostMinW ? 0 : subMinSize - subW	// check if the panel width is smaller than the minimum
				variable dH = isHostMinH ? 0 : subMinSize - subH
				if (dW <= 0 && dH <= 0)
					continue
				endif
				
				dragInfo = GetUserData(gName, "", sName)
				if (strlen(dragInfo))
					StructGet/S d, dragInfo
					subW = abs(d.subwinRect.right -d.subwinRect.left)
					subH = abs(d.subwinRect.bottom-d.subwinRect.top)
				endif
				
				tempRect.top 	= v_top	 - dH*(dH>0)
				tempRect.bottom = v_top  - dH*(dH>0) + subH
				tempRect.left	= v_left - dW*(dW>0)
				tempRect.right  = v_left - dW*(dW>0) + subW
				MoveSubwindow/W=$(gName+"#"+sName) fnum=(tempRect.left/pixConv, tempRect.top/pixConv, tempRect.right/pixConv, tempRect.bottom/pixConv)
			endfor
		break
	endswitch
	return hookTakeOver
end

// ++++++++++++

static structure DragStruct
	STRUCT point mouse_init
	STRUCT Rect subwinRect
	STRUCT Rect graphRect
endstructure

static function CheckRectPosAndSize(STRUCT rect &in, STRUCT rect &ref)
	int isInside  = (in.left>ref.left && in.top>ref.top && in.right<ref.right && in.bottom<ref.bottom)
	int isMinSize = abs(in.right-in.left) >= subMinSize && abs(in.bottom-in.top) >= subMinSize
	return isInside && isMinSize
end

static function CheckPointInRect(STRUCT point &pnt, STRUCT rect &ref)
	return ( pnt.h>ref.left && pnt.h<ref.right && pnt.v>ref.top && pnt.v<ref.bottom )
end

 

An example graph Dragable Subgraphs.ipf (7.04 KB)

Forum

Support

Gallery

Igor Pro 10

Learn More

Igor XOP Toolkit

Learn More

Igor NIDAQ Tools MX

Learn More