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

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. 

15 

16import builtins 1ab

17import wx 1ab

18import wx.grid 1ab

19from wx import Colour 1ab

20 

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

28 

29_ = wx.GetTranslation # To keep linter/code analyser happy 1ab

30builtins.__dict__['_'] = wx.GetTranslation 1ab

31 

32 

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

37 

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

58 

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__())) 

70 

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

78 

79 def set_validator(self, validator): 1ab

80 self._editor.SetValidator(validator) 1opmnqrhicdefgjkl

81 

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) 

88 

89 def set_focus(self): 1ab

90 self._editor.SetFocus() 1opmnqrhicdefgjkl

91 self._editor.SelectAll() 1opmnqrhicdefgjkl

92 

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() 

110 

111 

112class ArgumentEditor(ValueEditor): 1ab

113 

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

124 

125 

126class FileNameEditor(ValueEditor): 1ab

127 

128 _sizer_flags_for_editor = 0 1ab

129 _sizer_flags_for_label = wx.TOP | wx.BOTTOM | wx.LEFT 1ab

130 

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) 

137 

138 def setFocusToOK(self): 1ab

139 self._parent.setFocusToOK() 

140 

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 

146 

147 

148class VariableNameEditor(ValueEditor): 1ab

149 

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 

154 

155 def on_focus(self, event): 1ab

156 wx.CallAfter(self.SetSelection, event.GetEventObject()) 

157 event.Skip() 

158 

159 def SetSelection(self, event): 1ab

160 __ = event 

161 self._editor.SetSelection(2, len(self._editor.Value) - 1) 

162 

163 

164class ListValueEditor(ValueEditor): 1ab

165 expand_factor = 1 1ab

166 _sizer_flags_for_editor = wx.ALL | wx.EXPAND 1ab

167 

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) 

178 

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 

185 

186 def _create_label(self, label_text): 1ab

187 return Label(self, label=label_text, size=(80, -1)) 

188 

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 

210 

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) 

215 

216 def on_size(self, event): 1ab

217 self._editor.resize_columns(event.Size[0] - 110) 

218 event.Skip() 

219 

220 def get_value(self): 1ab

221 return self._editor.get_value() 

222 

223 

224class _EditorGrid(GridEditor): 1ab

225 

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 """ 

243 

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()) 

249 

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) 

253 

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)] 

261 

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() 

270 

271 def _colorize(self): 1ab

272 """ Just ignore it """ 

273 pass 

274 

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 

283 

284 def on_editor_shown(self, event): 1ab

285 if event.Row >= self.NumberRows - 1: 

286 self.AppendRows(1) 

287 

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 

292 

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) 

296 

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 

302 

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) 

306 

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() 

315 

316 def _insert_cells_to_multiple_rows(self, event): 1ab

317 GridEditor.on_insert_cells(self, event) 

318 

319 def _delete_cells_from_multiple_rows(self, event): 1ab

320 GridEditor.on_delete_cells(self, event) 

321 

322 def on_copy(self, event): 1ab

323 __ = event 

324 self.copy() 

325 

326 def on_cut(self, event): 1ab

327 __ = event 

328 self.cut() 

329 

330 def on_paste(self, event): 1ab

331 __ = event 

332 self.paste() 

333 

334 def on_delete(self, event): 1ab

335 __ = event 

336 self.delete() 

337 

338 def on_undo(self, event): 1ab

339 __ = event 

340 self.undo() 

341 

342 def on_select_all(self, event): 1ab

343 __ = event 

344 self.SelectAll() 

345 

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) 

349 

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) 

359 

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) 

365 

366 

367class MultiLineEditor(ValueEditor): 1ab

368 _sizer_flags_for_editor = wx.ALL | wx.EXPAND 1ab

369 

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 

381 

382 

383class ContentAssistEditor(ValueEditor): 1ab

384 

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) 

390 

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))