功能
- 测试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()
标签:end,Format,--,Text,Pos,Value,State,local
From: https://www.cnblogs.com/RobertL/p/17546284.html