Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #Requires AutoHotkey v2.0
- #w::WindowPreview("A")
- /**
- * Creates a live preview of a window using DWM thumbnails.
- * Click and drag the preview window to pan.
- * Use the scroll wheel to zoom in/out.
- * Double click to restore the source window.
- */
- class WindowPreview extends Gui {
- lastX := 0
- lastY := 0
- zoomedIn := 0
- edgeLimit := 50 ; prevent dragging preview outside of the window edge
- __New(source) {
- super.__New("+AlwaysOnTop Resize +ToolWindow -DPIScale MinSize150x150", "Window Preview: " WinGetTitle(source), this)
- this.BackColor := "171717"
- if !source := WinExist(source)
- return
- this.source := source
- this.Thumbnail := DWMThumbnail(this, source)
- this.OnEvent("Size", "onSize")
- this.OnEvent("Close", "Destroy")
- this.Handlers := {
- DoubleClick: this.OnDoubleClick.bind(this.hwnd),
- LButtonDown: this.OnLButton.bind(this.hwnd),
- LButtonUp: this.OnLButton.bind(this.hwnd),
- Wheel: this.OnMouseWheel.bind(this.hwnd),
- MouseMove: this.OnMouseMove.bind(this.hwnd)
- }
- OnMessage(0x0201, this.Handlers.LButtonDown) ; left click to drag the preview
- OnMessage(0x0202, this.Handlers.LButtonUp) ; left click to drag the preview
- OnMessage(0x0203, this.Handlers.DoubleClick) ; double click activates the original window
- OnMessage(0x020A, this.Handlers.Wheel) ; scroll to zoom in/out
- this.Thumbnail.querySourceSize(&w, &h)
- this.TL := this.TT := 0
- this.TR := w
- this.TB := h
- initialSize := 430
- if w > h
- iW := initialSize, iH := iW * h/w
- else
- iH := initialSize, iW := iH * w/h
- this.Show(Format("w{} h{} NA", iW, iH))
- }
- __Delete() {
- OnMessage(0x0201, this.Handlers.LButtonDown, 0)
- OnMessage(0x0202, this.Handlers.LButtonUp, 0)
- OnMessage(0x0203, this.Handlers.DoubleClick, 0)
- OnMessage(0x020A, this.Handlers.Wheel, 0)
- }
- OnDoubleClick(wParam, lParam, msg, hwnd) {
- if (this != hwnd)
- return
- this := GuiFromHwnd(this)
- if (this.Hwnd = hwnd)
- WinActivate(this.source)
- }
- onSize(MinMax, Width, Height) {
- this.Thumbnail.querySourceSize(&sW, &sH)
- if !this.zoomedIn {
- if sW * Height > Width * sH {
- ; h0 = height, h1 = vertical offset
- h0 := sH * Width / sW, h1 := (Height - h0) / 2
- this.TL := 0, this.TT := h1, this.TR := Width, this.TB := h0 + h1
- } else {
- ; w0 = width, w1 = horizontal offset
- w0 := sW * Height / sH, w1 := (Width - w0) / 2
- this.TL := w1, this.TT := 0, this.TR := w0 + w1, this.TB := Height
- }
- this.Thumbnail.SetThumbRegion(this.TL, this.TT, this.TR, this.TB)
- }
- }
- OnLButton(wParam, lParam, msg, hwnd) {
- if (this != hwnd)
- return
- this := GuiFromHwnd(this)
- if msg = 0x201 {
- this.lastx := lParam & 0xFFFF
- this.lasty := (lParam >> 16) & 0xFFFF
- OnMessage(0x200, this.Handlers.MouseMove)
- DllCall("SetCapture", "Ptr", this.hwnd) ; allow gui to receive WM_MOUSEMOVE even if cursor is outside the gui
- }
- else {
- OnMessage(0x200, this.Handlers.MouseMove, 0)
- DllCall("ReleaseCapture")
- }
- }
- OnMouseMove(wParam, lParam, msg, hwnd) {
- if (this != hwnd)
- return
- this := GuiFromHwnd(this)
- x := (lParam & 0xFFFF) << 48 >> 48
- y := lParam << 32 >> 48
- this.GetClientPos(,,&gW, &gH)
- ; mouse movement relative to last position
- dx := x - this.lastX
- dy := y - this.lastY
- if !(this.TL+dx > gW-this.edgeLimit) && !(this.TR+dx < this.edgeLimit)
- this.TL += dx, this.TR += dx
- if !(this.TT+dy > gH-this.edgeLimit) && !(this.TB+dy < this.edgeLimit)
- this.TT += dy, this.TB += dy
- this.zoomedIn := !(this.TL > 0 && this.TT > 0 && this.TR < gW && this.TB < gH)
- ; TL, TT, TR, RB : Left, Top, Right, Bottom of thumbnail
- this.Thumbnail.SetThumbRegion(this.TL, this.TT, this.TR, this.TB)
- this.lastX := x, this.lastY := y
- }
- ; zoom in/out on scroll
- OnMouseWheel(wParam, lParam, msg, hwnd) {
- if (this != hwnd)
- return
- this := GuiFromHwnd(this)
- delta := ((wParam << 32 >> 48) // 120)
- this.GetClientPos(&gX, &gY, &gW, &gH)
- ; convert lParam coordinates from screen to client coordinates
- mx := (lParam << 48 >> 48) - gX
- my := (lParam << 32 >> 48) - gY
- this.Thumbnail.querySourceSize(&Sw, &Sh)
- zoomFactor := 1 + delta * 0.05
- aspectRatio := Sw / Sh
- width := (this.TR - this.TL) * zoomFactor
- height := width / aspectRatio
- TL := mX - (mX - this.TL) * zoomFactor
- TT := mY - (mY - this.TT) * zoomFactor
- TR := TL + width
- TB := TT + height
- zoomLevel := (TR-TL)/sW
- minZoomLevel := 0.2, maxZoomLevel := 5
- if zoomLevel < minZoomLevel || zoomLevel > maxZoomLevel
- return
- this.TL := mX - (mX - this.TL) * zoomFactor
- this.TT := mY - (mY - this.TT) * zoomFactor
- this.TR := this.TL + width
- this.TB := this.TT + height
- width := this.TR - this.TL
- if this.TL > gW-this.edgeLimit {
- this.TL := gW-this.edgeLimit, this.TR := this.TL + width
- } else if this.TR < this.edgeLimit {
- this.TR := this.edgeLimit, this.TL := this.TR - width
- }
- height := this.TB - this.TT
- if this.TT > gH-this.edgeLimit {
- this.TT := gH-this.edgeLimit, this.TB := this.TT + height
- } else if this.TB < this.edgeLimit {
- this.TB := this.edgeLimit, this.TT := this.TB - height
- }
- this.zoomedIn := !(this.TL > 0 && this.TT > 0 && this.TR < gW && this.TB < gH)
- this.Thumbnail.SetThumbRegion(this.TL, this.TT, this.TR, this.TB)
- }
- }
- ; https://github.com/thqby/ahk2_lib/blob/master/DWMThumbnail.ahk
- class DWMThumbnail {
- __New(gui, source, clientOnly:=true) {
- hGui := gui.HasProp("hwnd") ? gui.hwnd : gui
- source := WinExist(source)
- if hr := DllCall("dwmapi\DwmRegisterThumbnail", "ptr", hGui, "ptr", source, "ptr*", &thumbnail := 0)
- throw OSError(hr)
- this.ptr := thumbnail
- this.setClientOnly(clientOnly)
- }
- querySourceSize(&width, &height) {
- if hr := DllCall("dwmapi\DwmQueryThumbnailSourceSize", "ptr",this, "ptr", size := Buffer(8))
- throw OSError(hr)
- width := NumGet(size, "int")
- height := NumGet(size, 4, "int")
- }
- SetSourceRegion(coords*) {
- this.updateProperties({s:coords})
- }
- SetThumbRegion(coords*) {
- this.updateProperties({d:coords})
- }
- updateProperties(properties) {
- buf := Buffer(45, 0), flags := 0
- for k, v in properties.OwnProps() {
- switch k, false {
- case "rcDestination", "dest", "d":
- ; DWM_TNP_RECTDESTINATION = 1
- flags |= 1, NumPut("int", v[1], "int", v[2], "int", v[3], "int", v[4], buf, 4)
- case "rcSource", "src", "s":
- if v ; DWM_TNP_RECTSOURCE = 2
- flags |= 2, NumPut("int", v[1], "int", v[2], "int", v[3], "int", v[4], buf, 20)
- case "opacity", "o":
- ; DWM_TNP_OPACITY = 4
- flags |= 4, NumPut("uchar", v, buf, 36)
- case "fVisible", "vis", "v":
- ; DWM_TNP_VISIBLE = 8
- flags |= 8, NumPut("int", v, buf, 37)
- case "fSourceClientAreaOnly", "client", "c":
- ; DWM_TNP_SOURCECLIENTAREAONLY = 16
- flags |= 16, NumPut("int", v, buf, 41)
- }
- }
- NumPut("uint", flags, buf)
- if hr := DllCall("dwmapi\DwmUpdateThumbnailProperties", "ptr", this, "ptr", buf)
- throw OSError(hr)
- }
- setVisible(vis := true) => this.updateProperties({v:vis})
- setClientOnly(client := true) => this.updateProperties({c:client})
- setOpacity(opacity) => this.updateProperties({o: opacity})
- __Delete() {
- DllCall("dwmapi\DwmUnregisterThumbnail", "ptr", this)
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement