Coverage for src/robotide/editor/fieldeditors.py: 40%
273 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-06 10:40 +0100
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-06 10:40 +0100
1# Copyright 2008-2015 Nokia Networks
2# Copyright 2016- Robot Framework Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
16import builtins 1ab
17import wx 1ab
18import wx.grid 1ab
19from wx import Colour 1ab
21from .contentassist import ContentAssistTextCtrl 1ab
22from .gridbase import GridEditor 1ab
23from ..context import ctrl_or_cmd, bind_keys_to_evt_menu 1ab
24from ..editor.contentassist import ContentAssistFileButton 1ab
25from ..namespace.suggesters import SuggestionSource 1ab
26from ..utils import split_value 1ab
27from ..widgets import Label 1ab
29_ = wx.GetTranslation # To keep linter/code analyser happy 1ab
30builtins.__dict__['_'] = wx.GetTranslation 1ab
33class ValueEditor(wx.Panel): 1ab
34 expand_factor = 0 1ab
35 _sizer_flags_for_editor = wx.ALL 1ab
36 _sizer_flags_for_label = wx.ALL 1ab
38 def __init__(self, parent, value, label=None, validator=None, settings=None, split=False): 1ab
39 wx.Panel.__init__(self, parent) 1opmnqrhicdefgjkl
40 self._label = label 1opmnqrhicdefgjkl
41 self.split = split 1opmnqrhicdefgjkl
42 self._sizer = wx.BoxSizer(wx.VERTICAL) 1opmnqrhicdefgjkl
43 from ..preferences import RideSettings 1opmnqrhicdefgjkl
44 _settings = RideSettings() 1opmnqrhicdefgjkl
45 self.general_settings = _settings['General'] 1opmnqrhicdefgjkl
46 self.color_background = self.general_settings['background'] 1opmnqrhicdefgjkl
47 self.color_foreground = self.general_settings['foreground'] 1opmnqrhicdefgjkl
48 self.color_secondary_background = self.general_settings['secondary background'] 1opmnqrhicdefgjkl
49 self.color_secondary_foreground = self.general_settings['secondary foreground'] 1opmnqrhicdefgjkl
50 self.color_background_help = self.general_settings['background help'] 1opmnqrhicdefgjkl
51 self.color_foreground_text = self.general_settings['foreground text'] 1opmnqrhicdefgjkl
52 self._create_editor(value, label, settings) 1opmnqrhicdefgjkl
53 if validator: 53 ↛ 55line 53 didn't jump to line 55 because the condition on line 53 was always true1opmnqrhicdefgjkl
54 self.set_validator(validator) 1opmnqrhicdefgjkl
55 self.SetSizer(self._sizer) 1opmnqrhicdefgjkl
56 self._sizer.Fit(self) 1opmnqrhicdefgjkl
57 self.Layout() 1opmnqrhicdefgjkl
59 def _create_editor(self, value, label, settings): 1ab
60 sizer = wx.BoxSizer(wx.HORIZONTAL) 1opmnqrhicdefgjkl
61 if self._label: 61 ↛ 64line 61 didn't jump to line 64 because the condition on line 61 was always true1opmnqrhicdefgjkl
62 sizer.Add(Label(self, label=self._label, size=(80, -1)), 0, 1opmnqrhicdefgjkl
63 self._sizer_flags_for_label, 5)
64 self._editor = self._get_text_ctrl() 1opmnqrhicdefgjkl
65 self._editor.AppendText(value) 1opmnqrhicdefgjkl
66 sizer.Add(self._editor, 1, self._sizer_flags_for_editor, 3) 1opmnqrhicdefgjkl
67 self._sizer.Add(sizer, 1, wx.EXPAND) 1opmnqrhicdefgjkl
68 self._editor.Bind(wx.EVT_KEY_DOWN, self.on_key_down) 1opmnqrhicdefgjkl
69 # print("DEBUG: ValueEditor _create_editor: %s\n" % (self._editor.__repr__()))
71 def _get_text_ctrl(self): 1ab
72 editor = wx.TextCtrl(self, size=(600, -1)) 1opmnqrhicdefgjkl
73 editor.SetBackgroundColour(Colour(self.color_secondary_background)) 1opmnqrhicdefgjkl
74 # editor.SetOwnBackgroundColour(Colour(self.color_secondary_background))
75 editor.SetForegroundColour(Colour(self.color_secondary_foreground)) 1opmnqrhicdefgjkl
76 # editor.SetOwnForegroundColour(Colour(self.color_secondary_foreground))
77 return editor 1opmnqrhicdefgjkl
79 def set_validator(self, validator): 1ab
80 self._editor.SetValidator(validator) 1opmnqrhicdefgjkl
82 def get_value(self): 1ab
83 # print("DEBUG: ValueEditor get_value: %s" % self.source_editor.GetValue())
84 value = self._editor.GetValue() 1mncdefg
85 if not self.split: 85 ↛ 87line 85 didn't jump to line 87 because the condition on line 85 was always true1mncdefg
86 return value 1mncdefg
87 return split_value(value)
89 def set_focus(self): 1ab
90 self._editor.SetFocus() 1opmnqrhicdefgjkl
91 self._editor.SelectAll() 1opmnqrhicdefgjkl
93 def on_key_down(self, event): 1ab
94 character = None
95 if event.CmdDown() and event.GetKeyCode() == ord('1'):
96 character = '$'
97 elif event.CmdDown() and event.GetKeyCode() == ord('2'):
98 character = '@'
99 elif event.CmdDown() and event.GetKeyCode() == ord('5'): # DEBUG New
100 character = '&'
101 if character:
102 if len(self.get_value()) == 0:
103 self._editor.WriteText(character + "{}")
104 else:
105 self._editor.AppendText(" | " + character + "{}")
106 _from, _ = self._editor.GetSelection()
107 self._editor.SetInsertionPoint(_from-1)
108 else:
109 event.Skip()
112class ArgumentEditor(ValueEditor): 1ab
114 def _create_editor(self, value, label, settings): 1ab
115 sizer = wx.BoxSizer(wx.HORIZONTAL) 1hicdefgjkl
116 if self._label: 116 ↛ 119line 116 didn't jump to line 119 because the condition on line 116 was always true1hicdefgjkl
117 sizer.Add(Label(self, label=self._label, size=(80, -1)), 0, 1hicdefgjkl
118 self._sizer_flags_for_label, 5)
119 self._editor = self._get_text_ctrl() 1hicdefgjkl
120 self._editor.AppendText(value) 1hicdefgjkl
121 sizer.Add(self._editor, 1, self._sizer_flags_for_editor, 3) 1hicdefgjkl
122 self._sizer.Add(sizer, 1, wx.EXPAND) 1hicdefgjkl
123 self._editor.Bind(wx.EVT_KEY_DOWN, self.on_key_down) 1hicdefgjkl
126class FileNameEditor(ValueEditor): 1ab
128 _sizer_flags_for_editor = 0 1ab
129 _sizer_flags_for_label = wx.TOP | wx.BOTTOM | wx.LEFT 1ab
131 def __init__(self, parent, value, label, controller, validator=None, settings=None, suggestion_source=None): 1ab
132 self._suggestion_source = suggestion_source or SuggestionSource(parent.plugin, None)
133 self._controller = controller
134 self._label = label
135 self._parent = parent
136 ValueEditor.__init__(self, parent, value, label, validator, settings)
138 def setFocusToOK(self): 1ab
139 self._parent.setFocusToOK()
141 def _get_text_ctrl(self): 1ab
142 filename_ctrl = ContentAssistFileButton(self, self._suggestion_source, '', self._controller, (500, -1))
143 filename_ctrl.SetBackgroundColour(Colour(self.color_secondary_background))
144 filename_ctrl.SetForegroundColour(Colour(self.color_secondary_foreground))
145 return filename_ctrl
148class VariableNameEditor(ValueEditor): 1ab
150 def _get_text_ctrl(self): 1ab
151 textctrl = ValueEditor._get_text_ctrl(self)
152 textctrl.Bind(wx.EVT_SET_FOCUS, self.on_focus)
153 return textctrl
155 def on_focus(self, event): 1ab
156 wx.CallAfter(self.SetSelection, event.GetEventObject())
157 event.Skip()
159 def SetSelection(self, event): 1ab
160 __ = event
161 self._editor.SetSelection(2, len(self._editor.Value) - 1)
164class ListValueEditor(ValueEditor): 1ab
165 expand_factor = 1 1ab
166 _sizer_flags_for_editor = wx.ALL | wx.EXPAND 1ab
168 def _create_editor(self, value, label, settings): 1ab
169 sizer = wx.BoxSizer(wx.HORIZONTAL)
170 self._settings = settings
171 cols = self._settings.get("list variable columns", 4)
172 # print(f"DEBUG: ListValueEditor before calling sizer.Add _create_components label={label} cols={cols}")
173 sizer.Add(self._create_components(label, cols))
174 self._editor = _EditorGrid(self, value, cols)
175 sizer.Add(self._editor, 1, self._sizer_flags_for_editor, 3)
176 self._sizer.Add(sizer, 1, wx.EXPAND)
177 self.Bind(wx.EVT_SIZE, self.on_size)
179 def _create_components(self, label, cols): 1ab
180 sizer = wx.BoxSizer(wx.VERTICAL)
181 sizer.Add(self._create_label(label), 0, wx.ALL, 5)
182 sizer.Add((-1, 10))
183 sizer.Add(self._create_column_selector(cols))
184 return sizer
186 def _create_label(self, label_text): 1ab
187 return Label(self, label=label_text, size=(80, -1))
189 def _create_column_selector(self, cols): 1ab
190 sizer = wx.BoxSizer(wx.VERTICAL)
191 col_label = Label(self, label=_("Columns"), size=(80, -1))
192 sizer.Add(col_label, 0, wx.ALL, 5)
193 combo = wx.ComboBox(self, value=str(cols), size=(60, 25),
194 choices=[str(i) for i in range(1, 11)])
195 tool_tip = wx.ToolTip(_("Number of columns that are shown in this editor."
196 " Selected value is stored and used globally."))
197 combo.SetToolTip(tool_tip)
198 tool_tip.GetWindow().SetBackgroundColour(Colour(self.color_background_help))
199 tool_tip.GetWindow().SetForegroundColour(Colour(self.color_foreground_text))
200 # DEBUG attributes = self.GetClassDefaultAttributes()
201 combo.SetBackgroundColour(Colour(self.color_secondary_background))
202 # combo.SetOwnBackgroundColour(Colour(self.color_secondary_background))
203 combo.SetForegroundColour(Colour(self.color_secondary_foreground))
204 # combo.SetOwnForegroundColour(Colour(self.color_secondary_foreground))
205 self.Bind(wx.EVT_COMBOBOX, self.on_columns, source=combo)
206 sizer.Add(combo)
207 # DEBUG children = self.GetChildren()
208 # print(f"DEBUG: Creating columns size selector: attributes bg ={attributes.colBg}\n children={children}")
209 return sizer
211 def on_columns(self, event): 1ab
212 num_cols = int(event.String)
213 self._settings["list variable columns"] = num_cols
214 self._editor.set_number_of_columns(num_cols)
216 def on_size(self, event): 1ab
217 self._editor.resize_columns(event.Size[0] - 110)
218 event.Skip()
220 def get_value(self): 1ab
221 return self._editor.get_value()
224class _EditorGrid(GridEditor): 1ab
226 def __init__(self, parent, value, num_cols): 1ab
227 num_rows = round(len(value) / num_cols + 2)
228 # print(f"DEBUG: _EditorGrid __init__ calc num_rows={num_rows} num_cols={num_cols}")
229 GridEditor.__init__(self, parent, num_rows, num_cols)
230 """
231 self.SetBackgroundColour(Colour(200, 222, 40))
232 self.SetOwnBackgroundColour(Colour(200, 222, 40))
233 self.SetForegroundColour(Colour(7, 0, 70))
234 self.SetOwnForegroundColour(Colour(7, 0, 70))
235 """
236 self._set_default_sizes()
237 self._bind_actions()
238 self._write_content(value)
239 self.Refresh(True)
240 """
241 self._colorize()
242 """
244 def _set_default_sizes(self): 1ab
245 self.SetColLabelSize(wx.grid.GRID_AUTOSIZE)
246 self.SetRowLabelSize(wx.grid.GRID_AUTOSIZE)
247 self.SetDefaultColSize(20)
248 self.SetDefaultRenderer(wx.grid.GridCellAutoWrapStringRenderer())
250 def _bind_actions(self): 1ab
251 bind_keys_to_evt_menu(self, self._get_bind_keys())
252 self.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, self.on_editor_shown)
254 def _get_bind_keys(self): 1ab
255 return [(ctrl_or_cmd(), ord('c'), self.on_copy),
256 (ctrl_or_cmd(), ord('x'), self.on_cut),
257 (ctrl_or_cmd(), ord('v'), self.on_paste),
258 (ctrl_or_cmd(), ord('z'), self.on_undo),
259 (ctrl_or_cmd(), ord('a'), self.on_select_all),
260 (wx.ACCEL_NORMAL, wx.WXK_DELETE, self.on_delete)]
262 def _write_content(self, value): 1ab
263 self.BeginBatch()
264 self.ClearGrid()
265 for index, item in enumerate(value):
266 row, col = divmod(index, self.NumberCols)
267 self.write_cell(row, col, item, False)
268 self.EndBatch()
269 self.AutoSizeRows()
271 def _colorize(self): 1ab
272 """ Just ignore it """
273 pass
275 def get_value(self): 1ab
276 value = []
277 for row in range(self.NumberRows):
278 for col in range(self.NumberCols):
279 value.append(self.GetCellValue(row, col))
280 while value and not value[-1]:
281 value.pop()
282 return value
284 def on_editor_shown(self, event): 1ab
285 if event.Row >= self.NumberRows - 1:
286 self.AppendRows(1)
288 def on_insert_cells(self, event): 1ab
289 if len(self.selection.rows()) != 1:
290 self._insert_cells_to_multiple_rows(event)
291 return
293 def insert_cells(data, start, end):
294 return data[:start] + [''] * (end - start) + data[start:]
295 self._insert_or_delete_cells_on_single_row(insert_cells, event)
297 def on_delete_cells(self, event): 1ab
298 # print("DEBUG delete cells %s" % self.selection.rows())
299 if len(self.selection.rows()) != 1:
300 self._delete_cells_from_multiple_rows(event)
301 return
303 def delete_cells(data, start, end):
304 return data[:start] + data[end:]
305 self._insert_or_delete_cells_on_single_row(delete_cells, event)
307 def _insert_or_delete_cells_on_single_row(self, action, event): 1ab
308 self._update_history()
309 value = self.get_value()
310 row, col = self.selection.cell
311 start = row * self.NumberCols + col
312 data = action(value, start, start + len(self.selection.cols()))
313 self._write_content(data)
314 event.Skip()
316 def _insert_cells_to_multiple_rows(self, event): 1ab
317 GridEditor.on_insert_cells(self, event)
319 def _delete_cells_from_multiple_rows(self, event): 1ab
320 GridEditor.on_delete_cells(self, event)
322 def on_copy(self, event): 1ab
323 __ = event
324 self.copy()
326 def on_cut(self, event): 1ab
327 __ = event
328 self.cut()
330 def on_paste(self, event): 1ab
331 __ = event
332 self.paste()
334 def on_delete(self, event): 1ab
335 __ = event
336 self.delete()
338 def on_undo(self, event): 1ab
339 __ = event
340 self.undo()
342 def on_select_all(self, event): 1ab
343 __ = event
344 self.SelectAll()
346 def resize_columns(self, width): 1ab
347 # print("DEBUG: Called resize coluumns, width=%d" % width)
348 self.SetDefaultColSize(max(int(width / self.NumberCols), 100), True)
350 def set_number_of_columns(self, columns): 1ab
351 new_cols = columns - self.NumberCols
352 if not new_cols:
353 return
354 width = self.NumberCols * self.GetDefaultColSize()
355 data = self.get_value()
356 self._set_cols(new_cols)
357 self.resize_columns(width)
358 self._write_content(data)
360 def _set_cols(self, new_cols): 1ab
361 if new_cols > 0:
362 self.AppendCols(numCols=new_cols)
363 else:
364 self.DeleteCols(numCols=-new_cols)
367class MultiLineEditor(ValueEditor): 1ab
368 _sizer_flags_for_editor = wx.ALL | wx.EXPAND 1ab
370 def _get_text_ctrl(self): 1ab
371 editor = wx.TextCtrl(self, style=wx.TE_MULTILINE | wx.TE_NOHIDESEL, size=(600, 400))
372 editor.SetBackgroundColour(Colour(self.color_secondary_background))
373 editor.SetForegroundColour(Colour(self.color_secondary_foreground))
374 """
375 editor.SetBackgroundColour(Colour(200, 222, 40))
376 editor.SetOwnBackgroundColour(Colour(200, 222, 40))
377 editor.SetForegroundColour(Colour(7, 0, 70))
378 editor.SetOwnForegroundColour(Colour(7, 0, 70))
379 """
380 return editor
383class ContentAssistEditor(ValueEditor): 1ab
385 def __init__(self, parent, value, label=None, validator=None, 1ab
386 settings=None, suggestion_source=None):
387 self._suggestion_source = suggestion_source or SuggestionSource(
388 parent.plugin, None)
389 ValueEditor.__init__(self, parent, value, label, validator, settings)
391 def _get_text_ctrl(self): 1ab
392 editor_ctrl = ContentAssistTextCtrl(self, self._suggestion_source)
393 editor_ctrl.SetBackgroundColour(Colour(self.color_background_help))
394 editor_ctrl.SetForegroundColour(Colour(self.color_foreground_text))
395 return editor_ctrl
396 # DEBUG size, (500, -1))