Coverage for src/robotide/preferences/editors.py: 18%
294 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.
16from os.path import abspath, dirname, join 1ab
17import builtins 1ab
18import wx 1ab
19from wx.lib.masked import NumCtrl 1ab
21from .settings import RideSettings 1ab
22from ..ui import preferences_dialogs as pdiag 1ab
23from robotide.ui.preferences_dialogs import PreferencesPanel 1ab
25from ..widgets import Label 1ab
26from .managesettingsdialog import SaveLoadSettings 1ab
27from functools import lru_cache 1ab
29try: # import installed version first 1ab
30 from pygments.lexers import robotframework as robotframeworklexer 1ab
31except ImportError: # Pygments is not installed
32 robotframeworklexer = None
34_ = wx.GetTranslation # To keep linter/code analyser happy 1ab
35builtins.__dict__['_'] = wx.GetTranslation 1ab
38ID_SAVELOADSETTINGS = wx.NewIdRef() 1ab
39ID_LOAD = 5551 1ab
40ID_SAVE = 5552 1ab
41ID_CANCEL = -1 1ab
42TEXT_BACKGROUND = _('Text background') 1ab
43LIGHT_GRAY = 'light gray' 1ab
44FIXED_FONT = 'fixed font' 1ab
47@lru_cache(maxsize=2) 1ab
48def read_fonts(fixed=False): 1ab
49 """Returns list with fixed width fonts"""
50 f = wx.FontEnumerator()
51 f.EnumerateFacenames()
52 names = f.GetFacenames(fixedWidthOnly=fixed)
53 names = [n for n in names if not n.startswith('@')]
54 names.sort()
55 return names
58def set_colors(element, bk_color, fg_color): 1ab
59 element.SetBackgroundColour(bk_color)
60 # element.SetOwnBackgroundColour(bk_color)
61 element.SetForegroundColour(fg_color)
62 # element.SetOwnForegroundColour(fg_color)
65class EditorPreferences(PreferencesPanel): 1ab
67 def __init__(self, settings, *args, **kwargs): 1ab
68 super(EditorPreferences, self).__init__(*args, **kwargs)
69 self._settings = settings
70 self._color_pickers = []
71 self.name = None
73 self._gsettings = RideSettings()
74 self.settings = self._gsettings['General']
76 # what would make this UI much more usable is if there were a
77 # preview window in the dialog that showed all the colors. I
78 # don't have the time to do that right now, so this will have
79 # to suffice.
81 font_editor = self._create_font_editor()
82 colors_sizer = self.create_colors_sizer()
83 main_sizer = wx.FlexGridSizer(rows=6, cols=1, vgap=10, hgap=10)
84 buttons_sizer = wx.BoxSizer(orient=wx.HORIZONTAL)
85 reset = wx.Button(self, wx.ID_ANY, _('Reset colors to default'))
86 saveloadsettings = wx.Button(self, ID_SAVELOADSETTINGS, _('Save or Load settings'))
87 set_colors(reset, self.secondary_background_color, self.secondary_foreground_color)
88 set_colors(saveloadsettings, self.secondary_background_color, self.secondary_foreground_color)
89 main_sizer.Add(font_editor)
90 main_sizer.Add(colors_sizer)
91 buttons_sizer.Add(reset)
92 buttons_sizer.AddSpacer(10)
93 buttons_sizer.Add(saveloadsettings)
94 main_sizer.Add(buttons_sizer)
95 self.SetSizer(main_sizer)
96 self.Bind(wx.EVT_BUTTON, self.on_reset)
97 self.Bind(wx.EVT_BUTTON, self.on_save_load_settings)
99 def on_save_load_settings(self, event): 1ab
100 raise NotImplementedError('Implement me')
102 def on_reset(self, event): 1ab
103 if not self.name:
104 self.name = "Grid"
105 defaults = self._read_defaults()
106 for picker in self._color_pickers:
107 picker.SetColour(defaults[picker.key])
109 def _read_defaults(self, plugin=False): 1ab
110 settings = [s.strip() for s in open(self._get_path(), 'r').readlines()]
111 name = ('[[%s]]' if plugin else '[%s]') % self.name
112 start_index = settings.index(name) + 1
113 defaults = {}
114 for line in settings[start_index:]:
115 if line.startswith('['):
116 break
117 if not line or line.startswith(';') or line.startswith('#'):
118 continue
119 key, value = [s.strip().strip('\'') for s in line.split("=")]
120 defaults[key] = value
121 return defaults
123 @staticmethod 1ab
124 def _get_path(): 1ab
125 return join(dirname(abspath(__file__)), 'settings.cfg')
127 def _create_font_editor(self): 1ab
128 f = pdiag.IntegerChoiceEditor(
129 self._settings, 'font size', _('Font Size'),
130 [str(i) for i in range(8, 16)])
131 sizer = wx.FlexGridSizer(rows=4, cols=2, vgap=10, hgap=30)
132 l_size = f.label(self)
133 set_colors(l_size, self.background_color, self.foreground_color)
134 sizer.AddMany([l_size, f.chooser(self)])
135 fixed_font = False
136 if 'zoom factor' in self._settings:
137 z = pdiag.SpinChoiceEditor(
138 self._settings, 'zoom factor', _('Zoom Factor'), (-10, 20))
139 l_zoom = z.label(self)
140 set_colors(l_zoom, self.background_color, self.foreground_color)
141 sizer.AddMany([l_zoom, z.chooser(self)])
142 if FIXED_FONT in self._settings:
143 l_ff, editor = pdiag.boolean_editor(self, self._settings, FIXED_FONT, _('Use fixed width font'))
144 set_colors(l_ff, self.background_color, self.foreground_color)
145 sizer.AddMany([l_ff, editor])
146 fixed_font = self._settings[FIXED_FONT]
147 if 'font face' in self._settings:
148 s = pdiag.StringChoiceEditor(self._settings, 'font face', _('Font Face'), read_fonts(fixed_font))
149 l_font = s.label(self)
150 set_colors(l_font, self.background_color, self.foreground_color)
151 sizer.AddMany([l_font, s.chooser(self)])
152 return sizer
154 def create_colors_sizer(self): 1ab
155 raise NotImplementedError('Implement me')
158class TextEditorPreferences(EditorPreferences): 1ab
159 location = (_("Text Editor"),) 1ab
161 def __init__(self, settings, *args, **kwargs): 1ab
162 self.location = (_("Text Editor"),)
163 self.title = _("Text Editor Settings")
164 self.name = "Text Edit"
165 super(TextEditorPreferences, self).__init__(settings[self.name], *args, **kwargs)
166 self.Sizer.Add(self._create_text_config_editor())
168 def create_colors_sizer(self): 1ab
169 container = wx.GridBagSizer()
170 column = 0
171 row = 0
172 if robotframeworklexer:
173 settings = (
174 ('argument', _('Argument foreground')),
175 ('comment', _('Comment foreground')),
176 ('error', _('Error foreground')),
177 ('gherkin', _('Gherkin keyword foreground')),
178 ('heading', _('Heading foreground')),
179 ('import', _('Import foreground')),
180 ('variable', _('Variable foreground')),
181 ('tc_kw_name', _('Keyword definition foreground')),
182 ('keyword', _('Keyword call foreground')),
183 ('separator', _('Separator')),
184 ('setting', _('Setting foreground')),
185 ('syntax', _('Syntax characters')),
186 ('background', TEXT_BACKGROUND),
187 )
188 else:
189 settings = (
190 ('setting', _('Text foreground')),
191 ('background', TEXT_BACKGROUND),
192 )
193 for settings_key, label_text in settings:
194 if column == 4:
195 column = 0
196 row += 1
197 label = wx.StaticText(self, wx.ID_ANY, label_text)
198 set_colors(label, self.background_color, self.foreground_color)
199 button = pdiag.PreferencesColorPicker(
200 self, wx.ID_ANY, self._settings, settings_key)
201 container.Add(button, (row, column),
202 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)
203 self._color_pickers.append(button)
204 column += 1
205 container.Add(label, (row, column),
206 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=8)
207 column += 1
208 return container
210 def on_save_load_settings(self, event): 1ab
211 if event.GetId() != ID_SAVELOADSETTINGS:
212 event.Skip()
213 return
214 save_settings_dialog = SaveLoadSettings(self, self._settings)
215 save_settings_dialog.CenterOnParent()
216 save_settings_dialog.ShowModal()
217 for picker in self._color_pickers:
218 picker.SetColour(self._settings[picker.key])
220 def on_reset(self, event): 1ab
221 if not self.name:
222 self.name = "Text Edit"
223 defaults = self._read_defaults()
224 for picker in self._color_pickers:
225 picker.SetColour(defaults[picker.key])
227 def _create_text_config_editor(self): 1ab
228 settings = self._settings
229 sizer = wx.FlexGridSizer(rows=2, cols=2, vgap=10, hgap=10)
230 l_auto_suggest, editor = pdiag.boolean_editor(self, settings, 'enable auto suggestions',
231 _('Enable auto suggestions'))
232 set_colors(l_auto_suggest, self.background_color, self.foreground_color)
233 sizer.AddMany([l_auto_suggest, editor])
234 return sizer
237class GridEditorPreferences(EditorPreferences): 1ab
238 location = (_("Grid Editor"),) 1ab
240 def __init__(self, settings, *args, **kwargs): 1ab
241 self.location = (_("Grid Editor"),)
242 self.title = _("Grid Editor Settings")
243 self.name = "Grid"
244 super(GridEditorPreferences, self).__init__(
245 settings[self.name], *args, **kwargs)
246 self.Sizer.Add(self._create_grid_config_editor())
248 def _create_grid_config_editor(self): 1ab
249 settings = self._settings
250 sizer = wx.FlexGridSizer(rows=6, cols=2, vgap=10, hgap=10)
251 l_col_size = self._label_for(_('Default column size'))
252 set_colors(l_col_size, self.background_color, self.foreground_color)
253 sizer.Add(l_col_size)
254 sizer.Add(self._number_editor(settings, 'col size'))
255 l_auto_size, editor = pdiag.boolean_editor(self, settings, 'auto size cols', _('Auto size columns'))
256 set_colors(l_auto_size, self.background_color, self.foreground_color)
257 sizer.AddMany([l_auto_size, editor])
258 l_max_size = self._label_for(_('Max column size\n(applies when auto size is on)'))
259 set_colors(l_max_size, self.background_color, self.foreground_color)
260 sizer.Add(l_max_size)
261 sizer.Add(self._number_editor(settings, 'max col size'))
262 l_word_wrap, editor = pdiag.boolean_editor(self, settings, 'word wrap', _('Word wrap and auto size rows'))
263 set_colors(l_word_wrap, self.background_color, self.foreground_color)
264 sizer.AddMany([l_word_wrap, editor])
265 l_auto_suggest, editor = pdiag.boolean_editor(self, settings, 'enable auto suggestions',
266 _('Enable auto suggestions'))
267 set_colors(l_auto_suggest, self.background_color, self.foreground_color)
268 sizer.AddMany([l_auto_suggest, editor])
269 return sizer
271 def _label_for(self, name): 1ab
272 label = ('%s: ' % name).capitalize()
273 return Label(self, label=label)
275 def _number_editor(self, settings, name): 1ab
276 initial_value = settings[name]
277 editor = NumCtrl(self, value=initial_value, integerWidth=3, allowNone=True)
278 set_colors(editor, self.background_color, self.foreground_color)
279 editor.Bind(wx.EVT_TEXT, lambda evt: self._set_value(editor, name))
280 return editor
282 def _set_value(self, editor, name): 1ab
283 # Guard against dead object
284 if editor:
285 value = editor.GetValue()
286 if value is not None:
287 self._settings.set(name, int(value))
289 def create_colors_sizer(self): 1ab
290 colors_sizer = wx.GridBagSizer()
291 self._create_foreground_pickers(colors_sizer)
292 self._create_background_pickers(colors_sizer)
293 return colors_sizer
295 def _create_foreground_pickers(self, colors_sizer): 1ab
296 row = 0
297 for key, label in (
298 ('text user keyword', _('User Keyword Foreground')),
299 ('text library keyword', _('Library Keyword Foreground')),
300 ('text variable', _('Variable Foreground')),
301 ('text unknown variable', _('Unknown Variable Foreground')),
302 ('text commented', _('Comments Foreground')),
303 ('text string', _('Default Foreground')),
304 ('text empty', _('Empty Foreground')),
305 ):
306 lbl = wx.StaticText(self, wx.ID_ANY, label)
307 set_colors(lbl, self.background_color, self.foreground_color)
308 btn = pdiag.PreferencesColorPicker(
309 self, wx.ID_ANY, self._settings, key)
310 self._color_pickers.append(btn)
311 colors_sizer.Add(btn, (row, 2),
312 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)
313 colors_sizer.Add(lbl, (row, 3),
314 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=4)
315 row += 1
317 def _create_background_pickers(self, colors_sizer): 1ab
318 row = 0
319 for key, label in (
320 ('background assign', _('Variable Background')),
321 ('background keyword', _('Keyword Background')),
322 ('background mandatory', _('Mandatory Field Background')),
323 ('background optional', _('Optional Field Background')),
324 ('background must be empty', _('Mandatory Empty Field Background')),
325 ('background unknown', _('Unknown Background')),
326 ('background error', _('Error Background')),
327 ('background highlight', _('Highlight Background'))
328 ):
329 lbl = wx.StaticText(self, wx.ID_ANY, label)
330 set_colors(lbl, self.background_color, self.foreground_color)
331 btn = pdiag.PreferencesColorPicker(
332 self, wx.ID_ANY, self._settings, key)
333 self._color_pickers.append(btn)
334 colors_sizer.Add(btn, (row, 0),
335 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)
336 colors_sizer.Add(lbl, (row, 1),
337 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=4)
338 row += 1
340 def on_save_load_settings(self, event): 1ab
341 if event.GetId() != ID_SAVELOADSETTINGS:
342 event.Skip()
343 return
344 save_settings_dialog = SaveLoadSettings(self, self._settings)
345 save_settings_dialog.CenterOnParent()
346 save_settings_dialog.ShowModal()
347 for picker in self._color_pickers:
348 picker.SetColour(self._settings[picker.key])
351class TestRunnerPreferences(EditorPreferences): 1ab
352 location = (_("Test Runner"),) 1ab
354 def __init__(self, settings, *args, **kwargs): 1ab
355 self.location = (_("Test Runner"),)
356 self.title = _("Test Runner Settings")
357 self.name = "Test Runner"
358 super(TestRunnerPreferences, self).__init__(
359 settings['Plugins'][self.name], *args, **kwargs)
360 help_color = wx.StaticText(self, wx.ID_ANY, _("Colors will be active after next RIDE restart."))
361 set_colors(help_color, self.background_color, self.foreground_color)
362 self.Sizer.Add(help_color)
363 self.Sizer.Add(self._create_test_runner_config_editor())
365 def _create_test_runner_config_editor(self): 1ab
366 self._settings.get('confirm run', True)
367 self._settings.get('use colors', False)
368 settings = self._settings
369 sizer = wx.FlexGridSizer(rows=6, cols=2, vgap=10, hgap=10)
370 from sys import platform
371 if platform.endswith('win32'):
372 add_colors = "-C ansi"
373 else:
374 add_colors = "-C on"
375 l_usecolor, usecolor = pdiag.boolean_editor(self, settings, 'use colors',
376 f"{_('Shows console colors set by')} {add_colors} ")
377 l_confirm, editor = pdiag.boolean_editor(self, settings, 'confirm run',
378 _('Asks for confirmation to run all tests if none selected '))
379 set_colors(l_confirm, self.background_color, self.foreground_color)
380 set_colors(l_usecolor, self.background_color, self.foreground_color)
381 sizer.AddMany([l_usecolor, usecolor])
382 sizer.AddMany([l_confirm, editor])
383 return sizer
385 def create_colors_sizer(self): 1ab
386 container = wx.GridBagSizer()
387 row = 0
388 column = 0
389 for settings_key, label_text in (
390 ('foreground', _('Text foreground')),
391 ('background', TEXT_BACKGROUND),
392 ('error', _('Error foreground')),
393 ('fail color', _('Fail foreground')),
394 ('pass color', _('Pass foreground')),
395 ('skip color', _('Skip foreground')),
396 ):
397 if column == 4:
398 column = 0
399 row += 1
400 label = wx.StaticText(self, wx.ID_ANY, label_text)
401 set_colors(label, self.background_color, self.foreground_color)
402 button = pdiag.PreferencesColorPicker(
403 self, wx.ID_ANY, self._settings, settings_key)
404 container.Add(button, (row, column),
405 flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL, border=4)
406 self._color_pickers.append(button)
407 column += 1
408 container.Add(label, (row, column),
409 flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=8)
410 column += 1
411 return container
413 def on_save_load_settings(self, event): 1ab
414 if event.GetId() != ID_SAVELOADSETTINGS:
415 event.Skip()
416 return
417 save_settings_dialog = SaveLoadSettings(self, self._settings)
418 save_settings_dialog.CenterOnParent()
419 save_settings_dialog.ShowModal()
420 for picker in self._color_pickers:
421 picker.SetColour(self._settings[picker.key])
423 def on_reset(self, event): 1ab
424 if not self.name:
425 self.name = "Test Runner"
426 defaults = self._read_defaults(plugin=True)
427 for picker in self._color_pickers:
428 picker.SetColour(defaults[picker.key])