Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to http://semver.org/spec/v2.0.0.html[Semantic Versioni
== https://github.com/robotframework/RIDE[Unreleased]

=== Added
- Added Config Panel button to supported installed Plugins next to their name in Plugin Manager dialog.
- Added Config Panel button to Plugins, working example in Text Editor.
- Added divided Status Bar. Left side for main window, right side for Plugins. Working example in Text Editor,
when selecting in Tree shows the filename in StatusBar.

Expand Down
2 changes: 1 addition & 1 deletion README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Likewise, the current version of wxPython, is 4.2.2, but RIDE is known to work w

`pip install -U robotframework-ride`

(3.8 <= python <= 3.13) Install current development version (**2.2dev16**) with:
(3.8 <= python <= 3.13) Install current development version (**2.2dev17**) with:

`pip install -U https://github.com/robotframework/RIDE/archive/develop.zip`

Expand Down
4 changes: 4 additions & 0 deletions src/robotide/application/CHANGELOG.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Changelog</title><link rel="stylesheet" type="text/css" href="docbook-xsl.css" /><meta name="generator" content="DocBook XSL Stylesheets Vsnapshot" /></head><body><div xml:lang="en" class="article" lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="id1337"></a>Changelog</h2></div></div><hr /></div><p>All notable changes to this project will be documented in this file.</p><p>The format is based on <a class="ulink" href="http://keepachangelog.com/en/1.0.0/" target="_top">Keep a Changelog</a>
and this project adheres to <a class="ulink" href="http://semver.org/spec/v2.0.0.html" target="_top">Semantic Versioning</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="_ulink_url_https_github_com_robotframework_ride_unreleased_ulink"></a>1. <a class="ulink" href="https://github.com/robotframework/RIDE" target="_top">Unreleased</a></h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_added"></a>1.1. Added</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Added Config Panel button to supported installed Plugins next to their name in Plugin Manager dialog.
</li><li class="listitem">
Added Config Panel button to Plugins, working example in Text Editor.
</li><li class="listitem">
Added divided Status Bar. Left side for main window, right side for Plugins. Working example in Text Editor,
when selecting in Tree shows the filename in StatusBar.
</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a id="_changed"></a>1.2. Changed</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">
Expand Down
4 changes: 4 additions & 0 deletions src/robotide/application/pluginconnector.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def __init__(self, plugin, application):
self.conn_plugin = plugin
self._settings = application.settings['Plugins'].add_section(plugin.name)
self.config_panel = plugin.config_panel
try:
self.on_config_panel = plugin.on_config_panel
except AttributeError:
pass
self.metadata = plugin.metadata

def enable_on_startup(self):
Expand Down
6 changes: 5 additions & 1 deletion src/robotide/application/releasenotes.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,12 @@ def set_content(self, html_win, content):
<ul class="simple">
<li>This version supports Python 3.8 up to 3.13.</li>
<li>There are some changes, or known issues:<ul>
<!--
<li>🐞 - When upgrading RIDE and activate Restart, some errors are visible about missing /language file, and behaviour
is not normal. Better to close RIDE and start a new instance.</li>
<li>🐞 - Problems with COPY/PASTE in Text Editor have been reported when using wxPython 4.2.0, but not with
version 4.2.1 and 4.2.2, which we now <em>recommend</em>.</li>
-->
<li>🐞 - Rename Keywords, Find Usages/Find where used are not finding all occurrences. Please, double-check findings and changes.</li>
<li>🐞 - Some argument types detection (and colorization) is not correct in Grid Editor.</li>
<li>🐞 - RIDE <strong>DOES NOT KEEP</strong> Test Suites formatting or structure, causing differences in files when used
Expand All @@ -168,6 +170,8 @@ def set_content(self, html_win, content):
</ul>
<p><strong>New Features and Fixes Highlights</strong></p>
<ul class="simple">
<li>Added Config Panel button to supported installed Plugins next to their name in Plugin Manager dialog.</li>
<li>Added Config Panel button to Plugins, working example in Text Editor.</li>
<li>On Windows ignore false modification on files when opening Test Suites, causing confirmation dialog.</li>
<li>Added divided Status Bar. Left side for main window, right side for Plugins. Working example in Text Editor,
when selecting in Tree shows the filename in StatusBar.</li>
Expand Down Expand Up @@ -225,7 +229,7 @@ def set_content(self, html_win, content):
<pre class="literal-block">python -m robotide.postinstall -install</pre>
<p>or</p>
<pre class="literal-block">ride_postinstall.py -install</pre>
<p>RIDE {VERSION} was released on 28/March/2025.</p>
<p>RIDE {VERSION} was released on 30/March/2025.</p>
<!-- <br/>
<h3>May The Fourth Be With You!</h3>
<h3>Celebrate the bank holiday, 10th June, Day of Portugal, Portuguese Communities and Camões!!</h3>
Expand Down
21 changes: 21 additions & 0 deletions src/robotide/editor/texteditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
from ..pluginapi import Plugin, action_info_collection, TreeAwarePluginMixin
from ..publish.messages import (RideSaved, RideTreeSelection, RideNotebookTabChanging, RideDataChanged, RideOpenSuite,
RideDataChangedToDirty, RideBeforeSaving, RideSaving, RideDataDirtyCleared)
from ..preferences import TextEditorPreferences, PreferenceEditor
from ..preferences.editors import read_fonts
from ..publish import RideSettingsChanged, PUBLISHER
from ..publish.messages import RideMessage
Expand Down Expand Up @@ -389,6 +390,8 @@ def __init__(self, application):
# self.status_bar = self.frame.FindWindowByName("StatusBar", self.frame)
# self.frame.SetStatusBarPane(1)
self.reformat = application.settings.get('reformat', False)
self.settings = application.settings
self.application = application
self._register_shortcuts()

@property
Expand Down Expand Up @@ -636,6 +639,18 @@ def is_focused(self):
except AttributeError:
return self._editor.is_focused()

def on_config_panel(self):
dlg = self.config_panel(self.frame)
dlg.Show(True)

def config_panel(self, parent):
__ = parent
_parent = wx.GetTopLevelWindows()
dlg = PreferenceEditor(_parent[0], _("RIDE - Preferences"),
self.application.preferences, style='single', index=4)
dlg.Show(False)
return dlg


class DummyController(WithStepsController):
_populator = robotapi.UserKeywordPopulator
Expand Down Expand Up @@ -1008,9 +1023,15 @@ def _create_editor_toolbar(self):
handler=lambda e: self.plugin._apply_txt_changes_to_model())
button.SetBackgroundColour(Colour(self.dlg.color_secondary_background))
button.SetForegroundColour(Colour(self.dlg.color_secondary_foreground))
config_button = ButtonWithHandler(self, _('Settings'), bitmap='wrench.png', fsize=self.general_font_size,
handler=lambda e: self.plugin.on_config_panel())
config_button.SetBackgroundColour(Colour(self.dlg.color_background))
config_button.SetOwnBackgroundColour(Colour(self.dlg.color_background))
config_button.SetForegroundColour(Colour(self.dlg.color_foreground))
default_components.add_with_padding(button)
self._create_search(default_components)
self.editor_toolbar.add_expanding(default_components)
self.editor_toolbar.add_with_padding(config_button)
self.Sizer.add_expanding(self.editor_toolbar, propotion=0)

def _create_search(self, container_sizer):
Expand Down
4 changes: 2 additions & 2 deletions src/robotide/namespace/namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -649,9 +649,9 @@ def __init__(self, keywords, caseless=True, new_lang=None):
try:
self.new_lang = Language.from_name(set_lang[0].replace('_','-'))
except ValueError:
self.new_lang = new_lang
self.new_lang = Language.from_name(new_lang[0])
else:
self.new_lang = new_lang
self.new_lang = Language.from_name(new_lang[0])
self.normalized_bdd_prefixes = utils.normalize_pipe_list(list(self.new_lang.bdd_prefixes), spaces=False)
self.gherkin_prefix = re.compile(fr'^({self.normalized_bdd_prefixes}) (.*)', re.IGNORECASE)
self.keywords = robotapi.NormalizedDict(ignore=['_'], caseless=caseless)
Expand Down
4 changes: 2 additions & 2 deletions src/robotide/preferences/editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

class PreferenceEditor(wx.Dialog):
"""A dialog for showing the preference panels"""
def __init__(self, parent, title, preferences, style="auto"):
def __init__(self, parent, title, preferences, style="auto", index=0):
wx.Dialog.__init__(self, parent, wx.ID_ANY, title, size=(850, 700),
style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE)
# set Left to Right direction (while we don't have localization)
Expand Down Expand Up @@ -116,7 +116,7 @@ def __init__(self, parent, title, preferences, style="auto"):
sizer.Add(self._container, 1, wx.EXPAND)
self.SetSizer(sizer)

panel = self._container.AddPanel(panels[0], self._settings)
panel = self._container.AddPanel(panels[index], self._settings)
self._container.ShowPanel(panel)

def on_close(self, evt):
Expand Down
29 changes: 19 additions & 10 deletions src/robotide/ui/pluginmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from ..context import LOG
from ..publish import RideLogException
from ..widgets import Label, RIDEDialog
from ..widgets import Label, RIDEDialog, ButtonWithHandler

_ = wx.GetTranslation # To keep linter/code analyser happy
builtins.__dict__['_'] = wx.GetTranslation
Expand Down Expand Up @@ -82,10 +82,9 @@ def _create_body(self, plugins, activation_callback):
sizer.Add(self._create_label(panel, _('Enabled')), 0, wx.BOTTOM, border=8)
sizer.Add(self._create_label(panel, _('Plugin')), 0, wx.BOTTOM | wx.EXPAND, border=8)
for plugin in sorted(plugins, key=lambda p: p.name):
sizer.Add(_PluginEnablationCheckBox(panel, plugin,
activation_callback),
flag=wx.ALIGN_CENTER_HORIZONTAL)
sizer.Add(_PluginRow(panel, plugin), 0, wx.EXPAND)
sizer.Add(_PluginEnablationCheckBox(panel, plugin, activation_callback),
flag=wx.ALIGN_LEFT)
sizer.Add(_PluginRow(panel, plugin, self.color_background), 0, wx.EXPAND)
panel.SetSizer(sizer)
return panel

Expand Down Expand Up @@ -141,16 +140,26 @@ def _execute(self, method):

class _PluginRow(wx.Panel):

def __init__(self, parent, plugin):
def __init__(self, parent, plugin, backgound_color):
wx.Panel.__init__(self, parent)
sz_head = wx.FlexGridSizer(0, 3, hgap=4, vgap=4)
l_name = self._get_name(plugin)
sz_head.Add(l_name)
b_spacing = Label(self, label=' ')
sz_head.Add(b_spacing)
if hasattr(plugin, 'on_config_panel'):
try:
sz_head.Add(ButtonWithHandler(self, _('Settings'), bitmap='wrench.png',
color_secondary_background=backgound_color,
handler=lambda e: plugin.on_config_panel()),
flag=wx.ALIGN_LEFT)
except AttributeError:
pass
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(self._get_name(plugin))
sizer.Add(sz_head)
for name, value in plugin.metadata.items():
sizer.Add(self._get_metadata(name, value))
sizer.Add(self._get_description(plugin), 0, wx.EXPAND)
config = plugin.config_panel(self)
if config:
sizer.Add(config, 1, wx.EXPAND | wx.LEFT, border=16)
self.SetSizer(sizer)

def _get_name(self, plugin):
Expand Down
2 changes: 1 addition & 1 deletion src/robotide/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
#
# Automatically generated by `tasks.py`.

VERSION = 'v2.2dev16'
VERSION = 'v2.2dev17'
23 changes: 18 additions & 5 deletions src/robotide/widgets/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,35 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import wx
from wx import Colour
from wx import Colour, BitmapBundle


class ButtonWithHandler(wx.Button):

def __init__(self, parent, label, mk_handler=None, handler=None, width=-1,
def __init__(self, parent, label, mk_handler=None, handler=None, width=-1, bitmap=None,
height=25, color_secondary_foreground='black', color_secondary_background='light grey', fsize=10):
fsize = max(8, fsize)
bt_image = None
style = 0
name = label
if bitmap is not None and isinstance(bitmap, str):
img_path = os.path.join(os.path.dirname(__file__), bitmap)
bt_image = BitmapBundle.FromFiles(img_path)
label = 'config_panel'
width = height
style = wx.BU_EXACTFIT | wx.BU_NOTEXT | wx.BORDER_NONE
if width == -1:
width = len(label) * fsize
size = wx.Size(width, height)
wx.Button.__init__(self, parent, label=label,
size=size)
wx.Button.__init__(self, parent, label=label, size=size, style=style, name=name)
if bt_image is not None:
self.SetBitmap(bt_image)
self.SetBitmapLabel(bt_image)
self.SetToolTip(name)
self.SetBackgroundColour(Colour(color_secondary_background))
# self.SetOwnBackgroundColour(Colour(color_secondary_background))
self.SetOwnBackgroundColour(Colour(color_secondary_background))
self.SetForegroundColour(Colour(color_secondary_foreground))
# self.SetOwnForegroundColour(Colour(color_secondary_foreground))
if not handler or mk_handler:
Expand Down
Binary file added src/robotide/widgets/wrench.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/robotide/widgets/wrench_orange.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 128 additions & 0 deletions utest/ui/test_mainframe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Copyright 2024- Robot Framework Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import unittest
import os
import pytest

DISPLAY = os.getenv('DISPLAY')
if not DISPLAY: # Avoid failing unit tests in system without X11
pytest.skip("Skipped because of missing DISPLAY", allow_module_level=True)
import wx
from wx.lib.agw.aui import AuiManager

from robotide.robotapi import (TestDataDirectory, TestCaseFile, ResourceFile,
TestCase, UserKeyword)
from robotide.spec.librarymanager import LibraryManager
from robotide.ui.mainframe import ActionRegisterer, ToolBar
from robotide.ui.actiontriggers import MenuBar, ShortcutRegistry
from robotide.application import Project
from robotide.application.pluginloader import PluginLoader
from robotide.controller.filecontrollers import (TestDataDirectoryController, ResourceFileController)
from utest.resources import FakeSettings
from robotide.editor.texteditor import TextEditorPlugin
from robotide.log import LogPlugin
from robotide.ui import mainframe
from robotide.publish import PUBLISHER
from robotide.ui.treeplugin import Tree
from robotide.namespace.namespace import Namespace


app = wx.App()


class _BaseDialogTest(unittest.TestCase):

def setUp(self):
self.app = wx.App()
self.app.settings = FakeSettings()
font_size = self.app.settings['General'].get('font size', 12)
font_face = self.app.settings['General'].get('font face', 'Helvetica')
self.app.fontinfo = wx.FontInfo(font_size).FaceName(font_face).Bold(False)
self.app.namespace = Namespace(FakeSettings())
self.model = self._create_model()
self.frame = mainframe.RideFrame(self.app, self.model)
self.app.frame = self.frame
txtplugin = TextEditorPlugin
logplugin = LogPlugin
plugins_dir = [os.path.join(os.path.dirname(__file__), 'plugins_for_loader')]
self.loader = PluginLoader(self.app, plugins_dir, [txtplugin, logplugin])
self.app.get_plugins = lambda: self.loader.plugins
self.frame.tree = Tree(self.frame, ActionRegisterer(AuiManager(self.frame),
MenuBar(self.frame), ToolBar(self.frame),
ShortcutRegistry(self.frame)), self.app.settings)
# self.frame.Show()

def tearDown(self):
PUBLISHER.unsubscribe_all()
self.app.ExitMainLoop()
self.app.Destroy()
self.app = None
# app.MainLoop() # With this here, there is no Segmentation fault

def _create_model(self):
suite = self._create_directory_suite('/top_suite')
suite.children = [self._create_file_suite('sub_suite_%d.robot' % i)
for i in range(3)]
res = ResourceFile()
res.source = 'resource.robot'
res.keyword_table.keywords.append(UserKeyword(res, 'Resource Keyword', ['en']))
library_manager = LibraryManager(':memory:')
library_manager.create_database()
model = Project(self.app.namespace, library_manager=library_manager)
model.controller = TestDataDirectoryController(suite)
rfc = ResourceFileController(res, project=model)
model.resources.append(rfc)
model.insert_into_suite_structure(rfc)
return model

def _create_directory_suite(self, source):
return self._create_suite(TestDataDirectory, source, is_dir=True)

def _create_file_suite(self, source):
suite = self._create_suite(TestCaseFile, source)
suite.testcase_table.tests = [TestCase(suite, '%s Fake Test %d' % (suite.name, i)) for i in range(16)]
return suite

@staticmethod
def _create_suite(suite_class, source, is_dir=False):
suite = suite_class()
suite.source = source
if is_dir:
suite.directory = source
suite.keyword_table.keywords = [UserKeyword(suite.keyword_table, '%s Fake UK %d' % (suite.name, i),
['en'])
for i in range(5)]
return suite


class TestMainFrame(_BaseDialogTest):

def test_show_mainframe(self):
self.frame.Show()
wx.CallLater(2000, self.app.ExitMainLoop)
self.app.MainLoop()

def test_show_plugins_manager(self):
self.frame.Show()
plugins = self.loader.plugins
self.frame._plugin_manager.show(plugins)
wx.CallLater(5000, self.app.ExitMainLoop)
wx.CallLater(4000, self.frame._plugin_manager._panel.Close)
self.app.MainLoop()


if __name__ == '__main__':
unittest.main()
app.Destroy()