File: C:/Users/fred/anaconda3/Lib/site-packages/spyder/plugins/editor/panels/manager.py
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# Copyright (c) 2013-2016 Colin Duquesnoy and others (see pyqode/AUTHORS.rst)
# Copyright (c) 2016- Spyder Project Contributors (see AUTHORS.txt)
#
# Distributed under the terms of the MIT License
# (see NOTICE.txt in the Spyder root directory for details)
# -----------------------------------------------------------------------------
"""
Contains the panels controller, drawing the panel inside CodeEditor's margins.
Not all panels are using PanelsManager, but future panels should use it.
Adapted from pyqode/core/managers/panels.py of the
`PyQode project <https://github.com/pyQode/pyQode>`_.
Original file:
<https://github.com/pyQode/pyqode.core/blob/master/pyqode/core/managers/panels.py>
"""
import logging
# Local imports
from spyder.api.manager import Manager
from spyder.api.panel import Panel
from spyder.py3compat import is_text_string
logger = logging.getLogger(__name__)
class PanelsManager(Manager):
"""
Manage the list of panels and draw them inside the margins of
CodeEditor widgets.
"""
def __init__(self, editor):
super(PanelsManager, self).__init__(editor)
self._cached_cursor_pos = (-1, -1)
self._margin_sizes = (0, 0, 0, 0)
self._top = self._left = self._right = self._bottom = -1
self._panels = {
Panel.Position.TOP: {},
Panel.Position.LEFT: {},
Panel.Position.RIGHT: {},
Panel.Position.BOTTOM: {},
Panel.Position.FLOATING: {}
}
try:
editor.blockCountChanged.connect(self._update_viewport_margins)
editor.updateRequest.connect(self._update)
except AttributeError:
# QTextEdit
editor.document().blockCountChanged.connect(
self._update_viewport_margins)
def register(self, panel, position=Panel.Position.LEFT):
"""
Installs a panel on the editor.
:param panel: Panel to install
:param position: Position where the panel must be installed.
:return: The installed panel
"""
assert panel is not None
pos_to_string = {
Panel.Position.BOTTOM: 'bottom',
Panel.Position.LEFT: 'left',
Panel.Position.RIGHT: 'right',
Panel.Position.TOP: 'top',
Panel.Position.FLOATING: 'floating'
}
logger.debug('adding panel %s at %s' % (panel.name,
pos_to_string[position]))
panel.order_in_zone = len(self._panels[position])
self._panels[position][panel.name] = panel
panel.position = position
panel.on_install(self.editor)
logger.debug('panel %s installed' % panel.name)
return panel
def remove(self, name_or_class):
"""
Removes the specified panel.
:param name_or_class: Name or class of the panel to remove.
:return: The removed panel
"""
logger.debug('Removing panel %s' % name_or_class)
panel = self.get(name_or_class)
panel.on_uninstall()
panel.hide()
panel.setParent(None)
return self._panels[panel.position].pop(panel.name, None)
def clear(self):
"""Removes all panels from the CodeEditor."""
for i in range(5):
while len(self._panels[i]):
key = sorted(list(self._panels[i].keys()))[0]
panel = self.remove(key)
panel.setParent(None)
def get(self, name_or_class):
"""
Gets a specific panel instance.
:param name_or_klass: Name or class of the panel to retrieve.
:return: The specified panel instance.
"""
if not is_text_string(name_or_class):
name_or_class = name_or_class.__name__
for zone in range(5):
try:
panel = self._panels[zone][name_or_class]
except KeyError:
pass
else:
return panel
raise KeyError(name_or_class)
def __iter__(self):
lst = []
for __, zone_dict in self._panels.items():
for __, panel in zone_dict.items():
lst.append(panel)
return iter(lst)
def __len__(self):
lst = []
for __, zone_dict in self._panels.items():
for __, panel in zone_dict.items():
lst.append(panel)
return len(lst)
def panels_for_zone(self, zone):
"""
Gets the list of panels attached to the specified zone.
:param zone: Panel position.
:return: List of panels instances.
"""
return list(self._panels[zone].values())
def refresh(self):
"""Refreshes the editor panels (resize and update margins)."""
self.resize()
self._update(self.editor.contentsRect(), 0,
force_update_margins=True)
def resize(self):
"""Resizes panels."""
crect = self.editor.contentsRect()
view_crect = self.editor.viewport().contentsRect()
s_bottom, s_left, s_right, s_top = self._compute_zones_sizes()
tw = s_left + s_right
th = s_bottom + s_top
w_offset = crect.width() - (view_crect.width() + tw)
h_offset = crect.height() - (view_crect.height() + th)
left = 0
panels = self.panels_for_zone(Panel.Position.LEFT)
panels.sort(key=lambda panel: panel.order_in_zone, reverse=True)
for panel in panels:
if not panel.isVisible():
continue
panel.adjustSize()
size_hint = panel.sizeHint()
panel.setGeometry(crect.left() + left,
crect.top() + s_top,
size_hint.width(),
crect.height() - s_bottom - s_top - h_offset)
left += size_hint.width()
right = 0
panels = self.panels_for_zone(Panel.Position.RIGHT)
panels.sort(key=lambda panel: panel.order_in_zone, reverse=True)
for panel in panels:
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
panel.setGeometry(
crect.right() - right - size_hint.width() - w_offset,
crect.top() + s_top,
size_hint.width(),
crect.height() - s_bottom - s_top - h_offset)
right += size_hint.width()
top = 0
panels = self.panels_for_zone(Panel.Position.TOP)
panels.sort(key=lambda panel: panel.order_in_zone)
for panel in panels:
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
panel.setGeometry(crect.left(),
crect.top() + top,
crect.width() - w_offset,
size_hint.height())
top += size_hint.height()
bottom = 0
panels = self.panels_for_zone(Panel.Position.BOTTOM)
panels.sort(key=lambda panel: panel.order_in_zone)
for panel in panels:
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
panel.setGeometry(
crect.left(),
crect.bottom() - bottom - size_hint.height() - h_offset,
crect.width() - w_offset,
size_hint.height())
bottom += size_hint.height()
def update_floating_panels(self):
"""Update foating panels."""
crect = self.editor.contentsRect()
panels = self.panels_for_zone(Panel.Position.FLOATING)
for panel in panels:
if not panel.isVisible():
continue
panel.set_geometry(crect)
def _update(self, rect, delta_y, force_update_margins=False):
"""Updates panels."""
if not self:
return
line, col = self.editor.get_cursor_line_column()
oline, ocol = self._cached_cursor_pos
for zones_id, zone in self._panels.items():
if zones_id == Panel.Position.TOP or \
zones_id == Panel.Position.BOTTOM:
continue
panels = list(zone.values())
for panel in panels:
if panel.scrollable and delta_y:
panel.scroll(0, delta_y)
if line != oline or col != ocol or panel.scrollable:
panel.update(0, rect.y(), panel.width(), rect.height())
self._cached_cursor_pos = line, col
if (rect.contains(self.editor.viewport().rect()) or
force_update_margins):
self._update_viewport_margins()
self.update_floating_panels()
def _update_viewport_margins(self):
"""Update viewport margins."""
top = 0
left = 0
right = 0
bottom = 0
for panel in self.panels_for_zone(Panel.Position.LEFT):
if panel.isVisible():
width = panel.sizeHint().width()
left += width
for panel in self.panels_for_zone(Panel.Position.RIGHT):
if panel.isVisible():
width = panel.sizeHint().width()
right += width
for panel in self.panels_for_zone(Panel.Position.TOP):
if panel.isVisible():
height = panel.sizeHint().height()
top += height
for panel in self.panels_for_zone(Panel.Position.BOTTOM):
if panel.isVisible():
height = panel.sizeHint().height()
bottom += height
new_size = (top, left, right, bottom)
if new_size != self._margin_sizes:
self._margin_sizes = new_size
self.editor.setViewportMargins(left, top, right, bottom)
def margin_size(self, position=Panel.Position.LEFT):
"""
Gets the size of a specific margin.
:param position: Margin position. See
:class:`spyder.api.Panel.Position`
:return: The size of the specified margin
:rtype: float
"""
return self._margin_sizes[position]
def _compute_zones_sizes(self):
"""Compute panel zone sizes."""
# Left panels
left = 0
for panel in self.panels_for_zone(Panel.Position.LEFT):
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
left += size_hint.width()
# Right panels
right = 0
for panel in self.panels_for_zone(Panel.Position.RIGHT):
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
right += size_hint.width()
# Top panels
top = 0
for panel in self.panels_for_zone(Panel.Position.TOP):
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
top += size_hint.height()
# Bottom panels
bottom = 0
for panel in self.panels_for_zone(Panel.Position.BOTTOM):
if not panel.isVisible():
continue
size_hint = panel.sizeHint()
bottom += size_hint.height()
self._top, self._left, self._right, self._bottom = (
top, left, right, bottom)
return bottom, left, right, top