Coverage for src/robotide/widgets/dialog.py: 52%

156 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 1ad

17import webbrowser 1ad

18 

19import wx 1ad

20from wx import html, Colour 1ad

21 

22from . import sizers, ButtonWithHandler 1ad

23 

24_ = wx.GetTranslation # To keep linter/code analyser happy 1ad

25builtins.__dict__['_'] = wx.GetTranslation 1ad

26 

27 

28class HtmlWindow(html.HtmlWindow): 1ad

29 

30 def __init__(self, parent, size=wx.DefaultSize, text=None, color_background=None, color_foreground=None): 1ad

31 html.HtmlWindow.__init__(self, parent, size=size, style=html.HW_DEFAULT_STYLE) 1acghJKLMNObij

32 from ..preferences import RideSettings 1acghJKLMNObij

33 _settings = RideSettings() 1acghJKLMNObij

34 self.general_settings = _settings['General'] 1acghJKLMNObij

35 self.color_background_help = color_background if color_background else self.general_settings['background help'] 1acghJKLMNObij

36 self.color_foreground_text = color_foreground if color_foreground else self.general_settings['foreground text'] 1acghJKLMNObij

37 self.SetBorders(2) 1acghJKLMNObij

38 self.SetStandardFonts(size=9) 1acghJKLMNObij

39 if text: 1acghJKLMNObij

40 self.set_content(text) 1aghbij

41 self.font = self.GetFont() 1acghJKLMNObij

42 self.font.SetFaceName(self.general_settings['font face']) 1acghJKLMNObij

43 self.font.SetPointSize(self.general_settings['font size']) 1acghJKLMNObij

44 self.SetFont(self.font) 1acghJKLMNObij

45 self.Refresh(True) 1acghJKLMNObij

46 self.Bind(wx.EVT_KEY_DOWN, self.on_key_down) 1acghJKLMNObij

47 self.Bind(wx.EVT_CLOSE, self.close) 1acghJKLMNObij

48 

49 def set_content(self, content): 1ad

50 if isinstance(self.color_background_help, tuple): 50 ↛ 51line 50 didn't jump to line 51 because the condition on line 50 was never true1acghbij

51 bgcolor = '#' + ''.join(hex(item)[2:] for item in self.color_background_help) 

52 else: 

53 bgcolor = self.color_background_help 1acghbij

54 if isinstance(self.color_foreground_text, tuple): 54 ↛ 55line 54 didn't jump to line 55 because the condition on line 54 was never true1acghbij

55 fgcolor = '#' + ''.join(hex(item)[2:] for item in self.color_foreground_text) 

56 else: 

57 fgcolor = self.color_foreground_text 1acghbij

58 if content.startswith('<table>'): 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true1acghbij

59 new_content = content.replace("<table>", f'<div><font color="{fgcolor}"><table>')\ 

60 .replace("</table>", "</table></font></div>") 

61 else: 

62 new_content = f'<p><font color="{fgcolor}">' + content + '</font></p>' 1acghbij

63 _content = '<body bgcolor=%s style="color:%s;">%s</body>' % (bgcolor, fgcolor, new_content) 1acghbij

64 self.SetPage(_content) 1acghbij

65 

66 def on_key_down(self, event): 1ad

67 if self._is_copy(event): 

68 self._add_selection_to_clipboard() 

69 self.Parent.on_key(event) 

70 event.Skip() 

71 

72 @staticmethod 1ad

73 def _is_copy(event): 1ad

74 return event.GetKeyCode() == ord('C') and event.CmdDown() 

75 

76 def _add_selection_to_clipboard(self): 1ad

77 wx.TheClipboard.Open() 

78 wx.TheClipboard.SetData(wx.TextDataObject(self.SelectionToText())) 

79 wx.TheClipboard.Close() 

80 

81 def OnLinkClicked(self, link): # Overrides wx method 1ad

82 webbrowser.open(link.Href) 

83 

84 def close(self): 1ad

85 self.Show(False) 

86 self.Destroy() 

87 

88 def clear(self): 1ad

89 self.SetPage('') 

90 

91 

92class RIDEDialog(wx.Dialog): 1ad

93 

94 def __init__(self, title='', parent=None, size=None, style=None, message=None): 1ad

95 size = size or (-1, -1) 1aklmnocbpqrstuvewxyzAfBCDEFGHI

96 style = style | wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER if style else (wx.DEFAULT_DIALOG_STYLE | 1aklmnocbpqrstuvewxyzAfBCDEFGHI

97 wx.RESIZE_BORDER ) 

98 wx.Dialog.__init__(self, parent=parent, title=title, size=size, style=style) 1aklmnocbpqrstuvewxyzAfBCDEFGHI

99 # set Left to Right direction (while we don't have localization) 

100 self.SetLayoutDirection(wx.Layout_LeftToRight) 1aklmnocbpqrstuvewxyzAfBCDEFGHI

101 self.message = message 1aklmnocbpqrstuvewxyzAfBCDEFGHI

102 from ..preferences import RideSettings 1aklmnocbpqrstuvewxyzAfBCDEFGHI

103 _settings = RideSettings() 1aklmnocbpqrstuvewxyzAfBCDEFGHI

104 self.general_settings = _settings['General'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

105 self.font_face = self.general_settings['font face'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

106 self.font_size = self.general_settings['font size'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

107 self.color_background = self.general_settings['background'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

108 self.color_foreground = self.general_settings['foreground'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

109 self.color_secondary_background = self.general_settings['secondary background'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

110 self.color_secondary_foreground = self.general_settings['secondary foreground'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

111 self.color_background_help = self.general_settings['background help'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

112 self.color_foreground_text = self.general_settings['foreground text'] 1aklmnocbpqrstuvewxyzAfBCDEFGHI

113 self.SetBackgroundColour(Colour(self.color_background)) 1aklmnocbpqrstuvewxyzAfBCDEFGHI

114 self.SetForegroundColour(Colour(self.color_foreground)) 1aklmnocbpqrstuvewxyzAfBCDEFGHI

115 if self.message: 115 ↛ 116line 115 didn't jump to line 116 because the condition on line 115 was never true1aklmnocbpqrstuvewxyzAfBCDEFGHI

116 sizer = wx.BoxSizer(wx.VERTICAL) 

117 content = wx.StaticText(self, -1, self.message) 

118 content.SetBackgroundColour(Colour(self.color_background)) 

119 content.SetForegroundColour(Colour(self.color_foreground)) 

120 sizer.Add(content, 0, wx.ALL | wx.EXPAND, 3) 

121 sizer.Add(wx.StaticText(self, -1, "\n\n"), 0, wx.ALL, 3) 

122 btn_sizer = self._create_buttons_sizer(style) 

123 sizer.Add(btn_sizer, 0, wx.ALIGN_CENTER | wx.BOTTOM, 3) 

124 self.SetSizer(sizer) 

125 sizer.Fit(self) 

126 self.Bind(wx.EVT_BUTTON, self.on_button) 

127 self.CenterOnParent() 1aklmnocbpqrstuvewxyzAfBCDEFGHI

128 

129 def _create_buttons_sizer(self, style=0): 1ad

130 sizer = wx.BoxSizer(wx.HORIZONTAL) 

131 btn_flag = False 

132 for btn, bid, label in zip([wx.NO, wx.CANCEL, wx.YES, wx.OK], [wx.ID_NO, wx.ID_CANCEL, wx.ID_YES, wx.ID_OK], 

133 [_('No'), _('Cancel'), _('Yes'), _('OK')]): 

134 if btn == style & btn: 

135 # print(f"DEBUG: dialog.py RIDEDialog _create_buttons_sizer ID={btn} label={label}") 

136 button = wx.Button(self, id=bid, label=label) 

137 button.SetBackgroundColour(Colour(self.color_secondary_background)) 

138 button.SetForegroundColour(Colour(self.color_secondary_foreground)) 

139 sizer.Add(button, 0, wx.ALIGN_CENTER | wx.BOTTOM, 5) 

140 btn_flag = True 

141 if not btn_flag: 

142 return self._create_buttons_sizer(wx.OK) 

143 return sizer 

144 

145 def _create_buttons(self, sizer): 1ad

146 buttons = self.CreateStdDialogButtonSizer(wx.OK | wx.CANCEL) 

147 self.SetBackgroundColour(Colour(self.color_background)) 

148 self.SetForegroundColour(Colour(self.color_foreground)) 

149 for item in self.GetChildren(): 

150 if isinstance(item, (wx.Button, wx.BitmapButton, ButtonWithHandler)): 

151 item.SetBackgroundColour(Colour(self.color_secondary_background)) 

152 # item.SetOwnBackgroundColour(Colour(self.color_secondary_background)) 

153 item.SetForegroundColour(Colour(self.color_secondary_foreground)) 

154 # item.SetOwnForegroundColour(Colour(self.color_secondary_foreground)) 

155 sizer.Add(buttons, flag=wx.ALIGN_CENTER | wx.ALL, border=5) 

156 sizer.Fit(self) 

157 

158 def _create_horizontal_line(self, sizer): 1ad

159 line = wx.StaticLine(self, size=(20, -1), style=wx.LI_HORIZONTAL) 

160 if wx.VERSION < (4, 1, 0): 

161 sizer.Add(line, border=5, flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT | wx.TOP) 

162 else: 

163 sizer.Add(line, border=5, flag=wx.GROW | wx.RIGHT | wx.TOP) 

164 sizer.Fit(self) 

165 

166 def on_button(self, event): 1ad

167 retval = event.GetId() 

168 # print(f"DEBUG: dialog.py RIDEDialog on_button ={retval}") 

169 if retval in (wx.ID_OK, wx.ID_YES): 

170 try: 

171 retval = self._execute() 

172 except NotImplementedError: 

173 pass 

174 if retval: 

175 self.EndModal(retval) 

176 event.Skip() 

177 

178 def execute(self): 1ad

179 retval = self.ShowModal() 1ef

180 # print(f"DEBUG: dialog.py RIDEDialog execute RETURN MODAL retval={retval}") 

181 if retval in (wx.ID_OK, wx.ID_YES): 181 ↛ 186line 181 didn't jump to line 186 because the condition on line 181 was always true1ef

182 try: 1ef

183 retval = self._execute() 1ef

184 except NotImplementedError: 

185 pass 

186 self.Destroy() 1ef

187 return retval 1ef

188 

189 def _execute(self): 1ad

190 raise NotImplementedError(self.__class__.__name__) 

191 

192 

193class HtmlDialog(RIDEDialog): 1ad

194 

195 def _execute(self): 1ad

196 """ Nothing to execute in this dialog """ 

197 pass 

198 

199 def __init__(self, title, content, padding=0, font_size=-1): 1ad

200 RIDEDialog.__init__(self, title) 1b

201 # set Left to Right direction (while we don't have localization) 

202 self.SetLayoutDirection(wx.Layout_LeftToRight) 1b

203 szr = sizers.VerticalSizer() 1b

204 self.SetMinClientSize((200, 300)) 1b

205 self.html_wnd = HtmlWindow(self, text=content) 1b

206 self.html_wnd.SetStandardFonts(size=font_size) 1b

207 szr.add_expanding(self.html_wnd, padding=padding) 1b

208 self.SetSizer(szr) 1b

209 szr.Fit(self) 1b

210 self.Layout() 1b

211 

212 def on_key(self, event): 1ad

213 """ In the event we need to process key events""" 

214 pass