IUP Text Format State test and enhancement
功能
- 测试IUP Text 控件 格式(Formating)在交互中的跟随性;
- 尝试提升格式跟随性。
涉及点
- Text的回调顺序关联;
- 撤销(undo)操作还原到的状态——上次手动改变光标时的状态;
- 输入时的状态(中文输入时涉及IME);
- k_any的C;
CapsLock/Shift对基本键的影响,及其他修饰键的作用,
IME激活时,C=229,参考。 - 模拟发送热键(
iup.SetGlobal('KEY',..)
); - 模拟发送热键时,Text启用
readonly
避免受IME影响; - Iup的使用。
代码
组成:(可合并)
- Iup_Text_Format_State_After_Interacting.lua
- Test__Iup_Text_Enhance_Interactive_For_Format_Inherit.lua
环境:
- iup 3.30,
- lua 5.4,
- Win 10。
Iup_Text_Format_State_After_Interacting
iup=require'iuplua' iup.SetGlobal('UTF8MODE','YES') Dialog=iup.dialog{ TOPMOST='YES', iup.vbox{ iup.text{ NAME='TEXT', VALUE='0123456789', EXPAND='HORIZONTAL', FORMATTING='YES', }, iup.button{ NAME='Add Format Button', TITLE='Add Format', EXPAND='HORIZONTAL', action=function(self) local Text=iup.GetDialogChild(self,'TEXT') local Tags=iup.user{ BULK='YES',CLEANOUT='YES', } local Text_Sections_Range_Start_To_End_Position={} local Text_String=Text.VALUE if #Text_String>=4 then Text_Sections_Range_Start_To_End_Position[0+1]=4 -- 1234567890 -- ^^^ end if #Text_String>6+3 then Text_Sections_Range_Start_To_End_Position[#Text_String-3-1]=#Text_String-1 -- .....67890 -- ^^^ end for Text_Range_Start_Position,Text_Range_End_Position in pairs(Text_Sections_Range_Start_To_End_Position) do local Tag=iup.user{ SELECTIONPOS=Text_Range_Start_Position..':'..Text_Range_End_Position, BGCOLOR='206 231 255', } Tags:append(Tag) end Text.ADDFORMATTAG=Tags end, }, }, map_cb=function() local Add_Format_Button=iup.GetDialogChild(Dialog,'Add Format Button') Add_Format_Button:action() end, } Dialog:show() if iup.MainLoopLevel()==0 then iup.MainLoop() end
Test__Iup_Text_Enhance_Interactive_For_Format_Inherit
--[[ config, see `Operate_Method`. ]] local string=require'LuaLibs.String.Utf8_String' local print=require'LuaLibs.Debug.PrintWithUserIndent' local iup=require'iuplua' do--import iup.timer{ TIME=1, action_cb=function(self) self.RUN='NO' require'LuaLibs.Module.Require_Current_Folder' --package.path=package.path..';'..string.match(arg[0],'^(.-)[^/\\]+$')..'?.lua' require'Iup_Text_Format_State_After_Interacting' -- import global `iup`, `Dialog`. return iup.CLOSE end,--action }.RUN='YES' iup.MainLoop() end--import do--Text local Text=iup.GetDialogChild(Dialog,'TEXT') function Text:action(C,New_Value) local Old_Value=self.VALUE local Caret=self.CARET local Caret_Pos=self.CARETPOS print("action:\t", "Old_Value:",Old_Value,"New_Value:",New_Value,--"Caret:",Caret, "Caret_Pos:",Caret_Pos, "C:",C ) end--Text:action local Previous_Text=Text.VALUE -- 不包含候选值。also check `Previous_Value`. local base_on_side,Selection_Pos_Before_Change,selected_text_before_change,caret_pos_before_change,lock_stored_caret_selection_status local simulate_key_press_state local Stored_Inserted_Value local text_valuechanged_cb,Text_valuechanged_cb_Invoke_Externally local Operate_Method= 'select then replace' -- will break undo -- or -- 'simulate key press' -- 有问题,也不稳定 do--Text.caret_cb local invoke_times=0 local Previous_Caret_Pos local Previous_Value -- 包含输入法的候选值。also check `Previous_Text`. function Text:caret_cb(Lin,Col,Pos) local Text=Text local Value=Text.VALUE local Current_Simulate_Key_Press_State=simulate_key_press_state print"caret_cb:\t".I( --"Caret:",Lin..":"..Col, "Pos:",Pos, "Selection:",Text.SELECTEDTEXT, "Value:",Value, table.unpack(Operate_Method=='select then replace' and {"Current_Simulate_Key_Press_State:",Current_Simulate_Key_Press_State} or {}) ) local Previous_Text=Previous_Text local Next_Simulate_Key_Press_State if Current_Simulate_Key_Press_State then if (Current_Simulate_Key_Press_State=='insert value' and assert(base_on_side=='left')) or Current_Simulate_Key_Press_State=='restore caret' and assert(base_on_side=='right') then --simulate_key_press_state=nil Next_Simulate_Key_Press_State=nil goto Update_State elseif Current_Simulate_Key_Press_State=='insert value and base' then --simulate_key_press_state='restore caret' --print("handle #2 - step 4.",simulate_key_press_state) Next_Simulate_Key_Press_State='restore caret' print("handle #2 - step 4.",Next_Simulate_Key_Press_State) iup.SetGlobal('KEY',iup.K_LEFT) elseif Current_Simulate_Key_Press_State=='reset base' then invoke_times=invoke_times+1 if invoke_times==3 then -- QUESTION: 不清楚为什么此处为'3',而非为'2'。 --simulate_key_press_state='insert value' --print("handle #1 - step 4.",simulate_key_press_state) Next_Simulate_Key_Press_State='insert value' print("handle #1 - step 4.",Next_Simulate_Key_Press_State) Text.INSERT=Stored_Inserted_Value -- won't invoke `valuechanged_cb`. Stored_Inserted_Value=nil invoke_times=0 text_valuechanged_cb=Text_valuechanged_cb_Invoke_Externally end else print("skip, when invoked internally by simulating key press.") end goto End end if Previous_Value==Value--exclude case - caret moved after `valuechanged_cb`. and Value==Previous_Text and not Text.SELECTEDTEXT then if Previous_Caret_Pos==Pos-1 then print"Caret move from left to right" base_on_side='left' elseif Previous_Caret_Pos==Pos+1 then print"Caret move from right to left" base_on_side='right' else base_on_side=nil end end if Previous_Text~=Value then lock_stored_caret_selection_status=true print("Lock_Stored_Caret_Selection_Status", "Caret_Pos:",caret_pos_before_change, "Selected_Text:",selected_text_before_change, "Selection_Pos:",Selection_Pos_Before_Change ) elseif lock_stored_caret_selection_status and not Selection_Pos_Before_Change then print"UnLock_Stored_Caret_Selection_Status" end ::Update_State:: if not lock_stored_caret_selection_status then print"Update_State" Selection_Pos_Before_Change=Text.SELECTIONPOS caret_pos_before_change=tonumber(Text.CARETPOS) selected_text_before_change=Text.SELECTEDTEXT end Previous_Caret_Pos=Pos Previous_Value=Value ::End:: if Next_Simulate_Key_Press_State then simulate_key_press_state=Next_Simulate_Key_Press_State end print.D() end--Text:caret_cb end--caret_cb do--Text_valuechanged_cb_Invoke_Externally local Text_valuechanged_cb_Invoke_Internally do local Character_Of_Base function Text_valuechanged_cb_Invoke_Internally(self,Value,Caret_Pos) local Previous_Simulate_Key_Press_State=simulate_key_press_state print.I( "Previous_Simulate_Key_Press_State:",Previous_Simulate_Key_Press_State ) assert(Operate_Method=='simulate key press') local Selected_Text_Before_Change=selected_text_before_change local Text=Text local Next_Simulate_Key_Press_State ::Begin:: if Previous_Simulate_Key_Press_State=='undo insert' then if base_on_side=='left' then assert(caret_pos_before_change>1 and Selected_Text_Before_Change,"Selected_Text_Before_Change: "..tostring(Selected_Text_Before_Change)) if Text.SELECTION then --simulate_key_press_state='delete selection' --print("handle #1 - step 2a.",simulate_key_press_state) Next_Simulate_Key_Press_State='delete selection' print("handle #1 - step 2a.",Next_Simulate_Key_Press_State) iup.SetGlobal('KEY',iup.K_BS) else--when IME --simulate_key_press_state='insert value' --print("handle #1 - step 2b.",simulate_key_press_state) Next_Simulate_Key_Press_State='insert value' print("handle #1 - step 2b.",Next_Simulate_Key_Press_State) Text.INSERT=Stored_Inserted_Value -- won't invoke `valuechanged_cb`. Stored_Inserted_Value=nil text_valuechanged_cb=Text_valuechanged_cb_Invoke_Externally -- --TODO: 连续时出错, need update something in `caret_cb` ? Selection_Pos_Before_Change=nil selected_text_before_change=nil print"end." end elseif base_on_side=='right' then --simulate_key_press_state='delete base' --print("handle #2 - step 2.",simulate_key_press_state) Next_Simulate_Key_Press_State='delete base' print("handle #2 - step 2.",Next_Simulate_Key_Press_State) assert(tonumber(Text.COUNT)>Caret_Pos) Character_Of_Base=string.sub(Value,Caret_Pos+1,Caret_Pos+1) print("Character_Of_Base:",Character_Of_Base) iup.SetGlobal('KEY',iup.K_RIGHT) iup.SetGlobal('KEY',iup.K_BS) else error'unhandle.' end elseif Previous_Simulate_Key_Press_State=='delete selection' then --simulate_key_press_state='reset base' --print("handle #1 - step 3.",simulate_key_press_state) Next_Simulate_Key_Press_State='reset base' print("handle #1 - step 3.",Next_Simulate_Key_Press_State) assert(base_on_side=='left') iup.SetGlobal('KEY',iup.K_LEFT) iup.SetGlobal('KEY',iup.K_RIGHT) --'handle #1 - step 4.' see `caret_cb`. elseif Previous_Simulate_Key_Press_State=='insert value' then error'never be invoked.' text_valuechanged_cb=Text_valuechanged_cb_Invoke_Externally elseif Previous_Simulate_Key_Press_State=='delete base' then assert(base_on_side=='right') --simulate_key_press_state='insert value and base' --print("handle #2 - step 3.",simulate_key_press_state) Next_Simulate_Key_Press_State='insert value and base' print("handle #2 - step 3.",Next_Simulate_Key_Press_State) Text.INSERT=Stored_Inserted_Value..Character_Of_Base -- won't invoke `valuechanged_cb`? Character_Of_Base=nil Stored_Inserted_Value=nil text_valuechanged_cb=Text_valuechanged_cb_Invoke_Externally --'handle #2 - step 4.' see `caret_cb`. elseif Previous_Simulate_Key_Press_State=='restore caret' then else error("unhandle. Previous_Simulate_Key_Press_State:"..tostring(Previous_Simulate_Key_Press_State)) end if Next_Simulate_Key_Press_State then simulate_key_press_state=Next_Simulate_Key_Press_State end print.D() end--Text_valuechanged_cb_Invoke_Internally end--Text_valuechanged_cb_Invoke_Internally local function Delete_Insert_Value(Caret_Pos,Inserted_Value) local Text=Text local Insert_Value_Left_Pos=Selection_Pos_Before_Change and string.match(Selection_Pos_Before_Change,'(%d+):') or caret_pos_before_change local Insert_Value_Right_Pos=Caret_Pos if not Text.SELECTEDTEXT==Inserted_Value then print("Text.SELECTEDTEXT:",Text.SELECTEDTEXT, "Inserted_Value:",Inserted_Value ) end Text.SELECTIONPOS=Insert_Value_Left_Pos..':'..Insert_Value_Right_Pos Text.SELECTEDTEXT='' return Insert_Value_Left_Pos,Insert_Value_Right_Pos end--Delete_Insert_Value() function Text_valuechanged_cb_Invoke_Externally(self,Value,Caret_Pos) local Caret_Pos_Before_Change=caret_pos_before_change local Selected_Text_Before_Change=selected_text_before_change local Inserted_Value do if Selected_Text_Before_Change then local Left_Pos=tonumber(string.match(Selection_Pos_Before_Change,'(%d+):')) Inserted_Value=string.sub(Value,Left_Pos+1,Caret_Pos) else Inserted_Value=string.sub(Value,Caret_Pos_Before_Change+1,Caret_Pos) end end print.I( "Inserted_Value:",Inserted_Value, "Caret_Pos_Before_Change:",Caret_Pos_Before_Change, table.unpack(Selected_Text_Before_Change and { "Selected_Text_Before_Change:",Selected_Text_Before_Change, "Selection_Pos_Before_Change:",Selection_Pos_Before_Change } or {}) ) local Text=Text if Operate_Method=='simulate key press' and #Inserted_Value>0 then repeat if base_on_side=='left' then if Caret_Pos_Before_Change>1 and Selected_Text_Before_Change then goto Handle end elseif base_on_side=='right' then if --not Selected_Text_Before_Change and tonumber(Text.COUNT)>Caret_Pos then goto Handle end end break ::Handle:: simulate_key_press_state='undo insert' print("handle step 1.",simulate_key_press_state) text_valuechanged_cb=Text_valuechanged_cb_Invoke_Internally Stored_Inserted_Value=Inserted_Value print("Stored_Inserted_Value:",Stored_Inserted_Value) Text.READONLY='YES' iup.SetGlobal('KEY',iup.XkeyCtrl(iup.K_z)) Text.READONLY='NO' until true elseif Operate_Method=='select then replace' then if base_on_side=='left' then if Caret_Pos_Before_Change>1 and Selected_Text_Before_Change then print"handle #1" local Insert_Value_Left_Pos,Insert_Value_Right_Pos=Delete_Insert_Value(Caret_Pos,Inserted_Value) Text.SELECTIONPOS=(Insert_Value_Left_Pos-1)..':'..Insert_Value_Left_Pos local Character_At_Left=Text.SELECTEDTEXT Text.SELECTEDTEXT=Character_At_Left..Inserted_Value Text.CARETPOS=Insert_Value_Right_Pos else--default, skip. end elseif base_on_side=='right' then if tonumber(Text.COUNT)>Caret_Pos then print"handle #2" local Insert_Value_Left_Pos,Insert_Value_Right_Pos=Delete_Insert_Value(Caret_Pos,Inserted_Value) Text.SELECTIONPOS=Insert_Value_Left_Pos..':'..(Insert_Value_Left_Pos+1) local Character_At_Right=Text.SELECTEDTEXT Text.SELECTEDTEXT=Inserted_Value..Character_At_Right Text.CARETPOS=Insert_Value_Right_Pos else --use left. end end end Previous_Text=Value if--undo will select something after handle Operate_Method=='select then replace' and Inserted_Value=='' and Text.SELECTION then print"fix caret in undo, after handle - which change selection." --local Selection_Left_Pos=string.match(Text.SELECTIONPOS,'(%d+):') --Text.CARETPOS=Selection_Left_Pos -- Text.CARETPOS=Base_On_Side=='right' and Caret_Pos-1 or Caret_Pos end if lock_stored_caret_selection_status then lock_stored_caret_selection_status=false print"UnLock_Stored_Caret_Selection_Status" --operation follow-up may not initial these state, so do it here. caret_pos_before_change=Caret_Pos Selection_Pos_Before_Change=nil selected_text_before_change=nil end print.D() end--Text_valuechanged_cb_Invoke_Externally text_valuechanged_cb=Text_valuechanged_cb_Invoke_Externally end--Text_valuechanged_cb_Invoke_Externally function Text:valuechanged_cb() local Value=self.VALUE local Caret_Pos=tonumber(self.CARETPOS) print("valuechanged_cb:", "Value:",Value, "Caret_Pos:",Caret_Pos ) return text_valuechanged_cb(self,Value,Caret_Pos) end do--Text.k_any local Parse_K_Any_C_Key_Name=require'LuaLibs.IUP.Interaction.Parse_K_Any_C_Key_Name' function Text:k_any(C) print("k_any:",C).I() local Text=Text if C==iup.K_ESC then print"K_ESC" local Selected_Text_Before_Change=selected_text_before_change if lock_stored_caret_selection_status and Selected_Text_Before_Change then print"restore previous stored caret selection status" Text.INSERT=Selected_Text_Before_Change -- won't invoke `valuechanged_cb`? Text.CARETPOS=caret_pos_before_change Text.SELECTIONPOS=Selection_Pos_Before_Change -- --Selected_Text_Before_Change,Caret_Pos_Before_Change,Selection_Pos_Before_Change=nil -- should not clear, for won't update state before `caret_cb`, but new input could be occurs. end elseif C==iup.K_BS then print"K_BS" if Operate_Method=='simulate key press' then iup.SetGlobal("KEY",iup.K_UP) -- set new restore point for undo. end else print(Parse_K_Any_C_Key_Name(C)) end print.D() end--Text:k_any end--Text.k_any function Text:getfocus_cb() print"getfocus_cb" end function Text:killfocus_cb() --print"killfocus_cb" end end--Text do--Add_Format_Button local Add_Format_Button=iup.GetDialogChild(Dialog,'Add Format Button') Add_Format_Button:action() end iup.MainLoop()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?