ahk悬浮粘贴板内容在桌面,实现复制

背景:

作为一个复制粘贴程序员,日常使用的就是复制粘贴,单是我发现,有时候需要频繁的切换窗口来复制我们需要的文字,而且是在同一个文件中进行复制操作,比如写java对象的时候我们要去复制这个对象的所有字段,来设置值,这时能不能把所有字段放悬浮在桌面,然后在另一个java代码中来操作,找了一下还没找到有这样的工具,但是有个工具snipast(贴图工具)和这个类似,但是他贴出来的都是图片,文字也被转换为图片了,我们只能看不能复制,很是不方便,后来就决定自己仿照snipast开发一个可以复制文本的悬浮窗呢,后来花了一周时间终于实现了。

实现原理 &操作方式:

ahk的gui中调用activeX空间渲染从粘贴板中获取的带格式的文本(html格式),然后通过快捷键悬浮在桌面上。

F3:显示gui

Shift+F3:隐藏/显示gui

拖动:选中任意文本内容,然后鼠标变为箭头即可拖动

ESC : 退出当前选中gui

最终效果 :

ahk悬浮粘贴板内容在桌面,实现复制

实现难点:

1、实现从粘贴板中获取带格式的文本,这个需要调用dll来实现,我也是找了很多资料才实现。

2、粘贴范围的锁定,即gui的长宽的确定

它的实现机制就是gui+activeX控件,大家都知道这个activeX控件很多js的东西不兼容,有的时候这个属性不行就换另一个属性,说不定就行了。

确定宽度width:

这个属实没法确定,只有通过计算粘贴板纯文本的最长字数来确定,区分汉字和范围在0-127的字符,然后确定单个字符长度(px)然后相加及是显示区域的宽度,为了美观我限制最大宽度为850。

确定高度height:

开始我使用计算行的方式,然后计算行高,最后能得出总高度,但是这样不是很准确,而且如果样式带背景会很不美观,所以我想到一个好方法,就是给样式的html中加入div,用div来包裹body中的所有内容,然后获取div的高度,就能完美得到整个gui的高度。

3、隐藏滑动快,最开始我想保留滑块,然后美化一下,但是ie没法改变滑块的样式,最后只有把他隐藏起来了,activeX控件的宽度大于gui的宽度20px以上就能隐藏右侧滑块。

4、实现activeX中文本复制功能

乍一看,不就是ctrl+c就能复制了吗,你都是文本了,可实际上不然,由于坑爹的activeX控件会拦截ctrl+c,enter,ctlr+x等操作,就只能通过右键选择复制来复制,然而这并不太方便,后来查阅资料都没有找到合适的解决方式,后来通过按键映射和js按键消息解决这个问题。

先是当前GUI界面监听ctrl+c按键操作,有ctrl+c我就给控件activeX发送非ctrl+c按键,这个我用的是P

;当前窗口GUI界面
#If WinActive(A_ScriptName)
  $^c::
    ;获取选中文本
;    tooltip ctl+c
    ControlSend,% "Internet Explorer_Server1" , P, %A_ScriptName%
    return
;    msgBox % VB
 
#If

然后在html页面 写js来监听按键P,监听到以后发送复制操作,最后两行代码是阻止用户输入的。

<script language="javascript" type="text/javascript">
		
		document.onkeypress = function() {
			var evt = (evt) ? evt : window.event;
			if (evt.keyCode == 80) {
				document.execCommand("Copy");
			}
			evt.keyCode = 0;
			evt.returnValue = false
		}
	</script>

啰嗦一下,为什么用document.onekeypress,因为其他属性activeX不支持,比如最开始我就是用的document.onkeydown ,死活复制不了,后来发现是这个代码就没起作用。

完整ahk代码 :

#SingleInstance, off
OnExit,OnExit
 
 
$F4::
;    xx1=C:\Users\Administrator\Desktop\xx1.html
;    xx2=C:\Users\Administrator\Desktop\xx2.html
;    xx3=C:\Users\Administrator\Desktop\xx3.html
    IfExist, %xx1%
        FileDelete ,%xx1%
    IfExist, %xx2%
            FileDelete ,%xx2%
    IfExist, %xx3%
            FileDelete ,%xx3%
    ;获取容器高度
    html:=ClipboardGet_HTML("html")
    FileAppend, %html%,%xx1% ,UTF-8
    if(html=0) ;普通文本格式
    {
        ;组装html
       html:=getHtml()
       FileAppend, %html%, %xx3%,UTF-8
    }
    html:=changeHtml(html)
    if(html=0)
    {
        html:=getHtml()
        html:=changeHtml(html)
    }
    FileAppend, %html%, %xx2%,UTF-8
    HTML_page:=html
    width_outter:=getClipboardMaxLineWidthPX()
    width_outter:=width_outter<250?250:width_outter
    width_outter:=width_outter>850?850:width_outter
 
    width_inner:=width_outter+20
    wpx:= "w" width_outter
    wpx2:="w" width_inner
;    wpx2:="w600"
 
    Gui  New
    Gui Add, ActiveX, x0 y0 %wpx2% h870 vWB, Shell.Explorer  ; The final parameter is the name of the ActiveX component.
    WB.silent := true ;Surpress JS Error boxes
 
    Display(WB,HTML_page)
    ;Wait for IE to load the page, before we connect the event handlers
    while WB.readystate != 4 or WB.busy
        sleep 10
    ;;获取内部div高度
    hdiv:=WB.document.getElementById("mainDiv").offsetHeight
    hdiv:=hdiv>850?850:hdiv
    hpx:="h" hdiv
 
    ;Use DOM access just like javascript!
    Gui +AlwaysOntop
    Gui -Caption +Border +ToolWindow
    Gui Show, %wpx% %hpx%
    return
 
;隐藏或显示桌面上的展示,贴图操作
DetectHiddenText, On
shift_f3_flag:=0
$+F4::
    if(!shift_f3_flag)
        WinHide ,%A_ScriptName%
    else
        WinShow  ,%A_ScriptName%
    shift_f3_flag:=!shift_f3_flag
    send {Shift down}{F3}{Shift up}
    return
 
GuiClose:
    Gui Destroy
OnExit:
    FileDelete,%A_Temp%\*.DELETEME.html ;clean tmp file
    Gui Destroy
    return
GuiEscape:
    Gui Destroy
    return
 
 
;------------------
Display(WB,html_str) {
    Count:=0
    while % FileExist(f:=A_Temp "\" A_TickCount A_NowUTC "-tmp" Count ".DELETEME.html")
        Count+=1
    FileAppend,%html_str%,%f%,UTF-8
    WB.Navigate("file://" . f)
}
;获取粘贴板的html格式数据
ClipboardGet_HTML( byref Data ) { ; http://www.autohotkey.com/forum/viewtopic.php?p=392624#392624
 If CBID := DllCall( "RegisterClipboardFormat", Str,"HTML Format", UInt )
  If DllCall( "IsClipboardFormatAvailable", UInt,CBID ) <> 0
   If DllCall( "OpenClipboard", UInt,0 ) <> 0
    If hData := DllCall( "GetClipboardData", UInt,CBID, UInt )
       DataL := DllCall( "GlobalSize", UInt,hData, UInt )
     , pData := DllCall( "GlobalLock", UInt,hData, UInt )
     , Data := StrGet( pData, dataL, "UTF-8" )
     , DllCall( "GlobalUnlock", UInt,hData )
     DllCall( "CloseClipboard" )
    Return dataL ? Data : 0
}
;获取粘贴板的html,并添加一些内容<head>...</head> 和 <div>....<div>
changeHtml(clipHtml)
{
    ;屏蔽浏览器间隙
    head=<html><head><style>*{margin: 0px;padding: 0px;}#mainDiv{overflow-x: visible;padding: 10px;}</style></head>
    ;主div用于测量高度
    beginDiv=<body id="body1"><div id="mainDiv" contenteditable="true" >
    endDiv=</div></body>
    js=<script language="javascript" type="text/javascript">if(document.getElementsByTagName("pre")[0]!=null){var rgb = document.getElementsByTagName("pre")[0].style.backgroundColor;document.getElementById("body1").style.backgroundColor = rgb;document.getElementById("body1").style.backgroundColor = rgb;}document.onkeypress = function() {var evt = (evt) ? evt : window.event;if(evt.keyCode==80){document.execCommand("Copy");}evt.keyCode = 0;evt.returnValue = false }</script>
    endDiv:=endDiv js
    beginCount:=0
    htmlTxt:=""
    Loop, parse, clipHtml, `n, `r
    {
         index:=A_Index
         if(index=6 &&!A_LoopField ) ;不处理office系列的各种格式
            return 0
         if index> 6
         {
             tempField:=A_LoopField
             if(index=7 && strBeginWith(tempField,"<html>"))
                StringReplace, tempField, tempField, <html> , % head
             if(inStr(tempField,"<body>") && !beginCount)
             {
                StringReplace, tempField, tempField, <body>, % beginDiv
                beginCount+=1
             }
             htmlTxt:=htmlTxt tempField "`r`n"
         }
    }
    StringGetPos, position, htmlTxt, </body> ,R
    str1:=SubStr(htmlTxt,1,positIon)
    str2:=SubStr(htmlTxt,position+1)
    StringReplace, str2, str2, </body>, % endDiv
    htmlTxt:=str1 str2
    return htmlTxt
}
;获取粘贴板最长行的像素
getClipboardMaxLineWidthPX()
{
    a_length:=9 ;字符占用宽度
    b_length:=18 ;汉字占用宽度
    clip:=clipboard
    max_width_px:=0
    Loop, parse, clip, `n, `r
    {
        index:=A_Index
        line:=A_loopfield
        temp_width_px:=0
        Loop, parse, line
        {
            char:=A_loopfield
            if  chr(char)<128
                temp_width_px:=temp_width_px+a_length
             else
                temp_width_px:=temp_width_px+b_length
        }
        ;msgBox % "temp_width_px:"temp_width_px  " max_width_px:"max_width_px
        if(temp_width_px >max_width_px)
            max_width_px:=temp_width_px
    }
    return max_width_px
}
;粘贴板文本转为html格式文件
getHtml()
{
    clip:=clipboard
    head=<html><head><meta charset="utf-8"><style>body { background-color:#3c3f41; font-family:"Trebuchet MS", Arial, Helvetica, sans-serif;font-size:15px; color:#ffffff;line-height:18px;letter-spacing:1.06px}</style></head><body>
    head:= "1`r`n2`r`n3`r`n4`r`n5`r`n6`r`n" head
    Loop, parse, clip, `n, `r
    {
        line:=A_loopField
        StringReplace, line, line,% " " ,% "&nbsp;",All
        head:=head "<p>" line "</p>"
    }
    head:=head "</body></html>"
    return head
}
strBeginWith(str,suffix)
{
    return str!=LTrim(str,suffix)
}
;移动选框
WM_LBUTTONDOWN(){
    Static init:=OnMessage(0x0201, "WM_LBUTTONDOWN")
    if(A_Cursor="Arrow")
        PostMessage, 0xA1, 2
}
;当前窗口GUI界面
#If WinActive(A_ScriptName)
  $^c::
    ;获取选中文本
;    tooltip ctl+c
    ControlSend,% "Internet Explorer_Server1" , P, %A_ScriptName%
    return
;    msgBox % VB
#If

 

 

 

给TA捐赠
共{{data.count}}人
人已捐赠
其他函数案例

Sinet——Python_AHK 陌诺开源计划

2022-6-17 6:55:13

案例

把window运行框为我所用(字符串计算器,翻译,解码,打开应用等)

2022-6-17 19:45:47

9 条回复 A文章作者 M管理员
  1. AHK中文社区

    建议使用标题而不是引用

  2. AHK中文社区
    1河许人给您捐赠了¥5
  3. dbgba
    dbgba给您捐赠了¥5
  4. 月下马

    不用Ditto吗?

    • Tmz

      win10有自带的多复制多粘贴工具,这只是把粘贴板内容钉在桌面,方便对比和复制。

  5. 逍遥子

    实用的功能

  6. Gaobang

    大佬厉害啊(一点小错误:截图软件叫snipaste,少了一个e)

  7. homjie

    请问大佬为什么我的和你的效果不一样,有这个白框

    • Tmz

      可能纵向距离没有算好,你看看代码改改,这个对idea ,plsql,因为经常对这两个软件操作所以适配就比较完美。

个人中心
购物车
优惠劵
有新私信 私信列表
搜索