bpo-37039: Make IDLE's Zoom Height adjust to users' screens (GH-13678)

Measure required height by quickly maximizing once per screen.
A search for a better method failed.
This commit is contained in:
Tal Einat 2019-06-17 22:41:00 +03:00 committed by Terry Jan Reedy
parent a268edd6a4
commit 5bff3c86ab
5 changed files with 124 additions and 28 deletions

View File

@ -289,7 +289,10 @@ Show/Hide Code Context (Editor Window only)
Zoom/Restore Height Zoom/Restore Height
Toggles the window between normal size and maximum height. The initial size Toggles the window between normal size and maximum height. The initial size
defaults to 40 lines by 80 chars unless changed on the General tab of the defaults to 40 lines by 80 chars unless changed on the General tab of the
Configure IDLE dialog. Configure IDLE dialog. The maximum height for a screen is determined by
momentarily maximizing a window the first time one is zoomed on the screen.
Changing screen settings may invalidate the saved height. This toogle has
no effect when a window is maximized.
Window menu (Shell and Editor) Window menu (Shell and Editor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -3,6 +3,14 @@ Released on 2019-10-20?
====================================== ======================================
bpo-37039: Adjust "Zoom Height" to individual screens by momemtarily
maximizing the window on first use with a particular screen. Changing
screen settings may invalidate the saved height. While a window is
maximized, "Zoom Height" has no effect.
bpo-35763: Make calltip reminder about '/' meaning positional-only less
obtrusive by only adding it when there is room on the first line.
bpo-35610: Replace now redundant editor.context_use_ps1 with bpo-35610: Replace now redundant editor.context_use_ps1 with
.prompt_last_line. This finishes change started in bpo-31858. .prompt_last_line. This finishes change started in bpo-31858.

View File

@ -6,7 +6,7 @@
<head> <head>
<meta http-equiv="X-UA-Compatible" content="IE=Edge" /> <meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>IDLE &#8212; Python 3.8.0a4 documentation</title> <title>IDLE &#8212; Python 3.9.0a0 documentation</title>
<link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" /> <link rel="stylesheet" href="../_static/pydoctheme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" /> <link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
@ -19,7 +19,7 @@
<script type="text/javascript" src="../_static/sidebar.js"></script> <script type="text/javascript" src="../_static/sidebar.js"></script>
<link rel="search" type="application/opensearchdescription+xml" <link rel="search" type="application/opensearchdescription+xml"
title="Search within Python 3.8.0a4 documentation" title="Search within Python 3.9.0a0 documentation"
href="../_static/opensearch.xml"/> href="../_static/opensearch.xml"/>
<link rel="author" title="About these documents" href="../about.html" /> <link rel="author" title="About these documents" href="../about.html" />
<link rel="index" title="Index" href="../genindex.html" /> <link rel="index" title="Index" href="../genindex.html" />
@ -50,6 +50,7 @@
</head><body> </head><body>
<div class="related" role="navigation" aria-label="related navigation"> <div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3> <h3>Navigation</h3>
<ul> <ul>
@ -72,7 +73,7 @@
<li> <li>
<a href="../index.html">3.8.0a4 Documentation</a> &#187; <a href="../index.html">3.9.0a0 Documentation</a> &#187;
</li> </li>
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li> <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@ -320,7 +321,10 @@ of the code which has scrolled above the top of the window. See
<dt>Zoom/Restore Height</dt> <dt>Zoom/Restore Height</dt>
<dd>Toggles the window between normal size and maximum height. The initial size <dd>Toggles the window between normal size and maximum height. The initial size
defaults to 40 lines by 80 chars unless changed on the General tab of the defaults to 40 lines by 80 chars unless changed on the General tab of the
Configure IDLE dialog.</dd> Configure IDLE dialog. The maximum height for a screen is determined by
momentarily maximizing a window the first time one is zoomed on the screen.
Changing screen settings may invalidate the saved height. This toogle has
no effect when a window is maximized.</dd>
</dl> </dl>
</div> </div>
<div class="section" id="window-menu-shell-and-editor"> <div class="section" id="window-menu-shell-and-editor">
@ -912,7 +916,7 @@ also used for testing.</p>
<li> <li>
<a href="../index.html">3.8.0a4 Documentation</a> &#187; <a href="../index.html">3.9.0a0 Documentation</a> &#187;
</li> </li>
<li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li> <li class="nav-item nav-item-1"><a href="index.html" >The Python Standard Library</a> &#187;</li>
@ -943,7 +947,7 @@ also used for testing.</p>
<br /> <br />
<br /> <br />
Last updated on May 25, 2019. Last updated on Jun 17, 2019.
<a href="https://docs.python.org/3/bugs.html">Found a bug</a>? <a href="https://docs.python.org/3/bugs.html">Found a bug</a>?
<br /> <br />

View File

@ -2,42 +2,119 @@
import re import re
import sys import sys
import tkinter
from idlelib import macosx
class WmInfoGatheringError(Exception):
pass
class ZoomHeight: class ZoomHeight:
# Cached values for maximized window dimensions, one for each set
# of screen dimensions.
_max_height_and_y_coords = {}
def __init__(self, editwin): def __init__(self, editwin):
self.editwin = editwin self.editwin = editwin
self.top = self.editwin.top
def zoom_height_event(self, event=None): def zoom_height_event(self, event=None):
top = self.editwin.top zoomed = self.zoom_height()
zoomed = zoom_height(top)
if zoomed is None:
self.top.bell()
else:
menu_status = 'Restore' if zoomed else 'Zoom' menu_status = 'Restore' if zoomed else 'Zoom'
self.editwin.update_menu_label(menu='options', index='* Height', self.editwin.update_menu_label(menu='options', index='* Height',
label=f'{menu_status} Height') label=f'{menu_status} Height')
return "break" return "break"
def zoom_height(self):
top = self.top
def zoom_height(top): width, height, x, y = get_window_geometry(top)
if top.wm_state() != 'normal':
# Can't zoom/restore window height for windows not in the 'normal'
# state, e.g. maximized and full-screen windows.
return None
try:
maxheight, maxy = self.get_max_height_and_y_coord()
except WmInfoGatheringError:
return None
if height != maxheight:
# Maximize the window's height.
set_window_geometry(top, (width, maxheight, x, maxy))
return True
else:
# Restore the window's height.
#
# .wm_geometry('') makes the window revert to the size requested
# by the widgets it contains.
top.wm_geometry('')
return False
def get_max_height_and_y_coord(self):
top = self.top
screen_dimensions = (top.winfo_screenwidth(),
top.winfo_screenheight())
if screen_dimensions not in self._max_height_and_y_coords:
orig_state = top.wm_state()
# Get window geometry info for maximized windows.
try:
top.wm_state('zoomed')
except tkinter.TclError:
# The 'zoomed' state is not supported by some esoteric WMs,
# such as Xvfb.
raise WmInfoGatheringError(
'Failed getting geometry of maximized windows, because ' +
'the "zoomed" window state is unavailable.')
top.update()
maxwidth, maxheight, maxx, maxy = get_window_geometry(top)
if sys.platform == 'win32':
# On Windows, the returned Y coordinate is the one before
# maximizing, so we use 0 which is correct unless a user puts
# their dock on the top of the screen (very rare).
maxy = 0
maxrooty = top.winfo_rooty()
# Get the "root y" coordinate for non-maximized windows with their
# y coordinate set to that of maximized windows. This is needed
# to properly handle different title bar heights for non-maximized
# vs. maximized windows, as seen e.g. in Windows 10.
top.wm_state('normal')
top.update()
orig_geom = get_window_geometry(top)
max_y_geom = orig_geom[:3] + (maxy,)
set_window_geometry(top, max_y_geom)
top.update()
max_y_geom_rooty = top.winfo_rooty()
# Adjust the maximum window height to account for the different
# title bar heights of non-maximized vs. maximized windows.
maxheight += maxrooty - max_y_geom_rooty
self._max_height_and_y_coords[screen_dimensions] = maxheight, maxy
set_window_geometry(top, orig_geom)
top.wm_state(orig_state)
return self._max_height_and_y_coords[screen_dimensions]
def get_window_geometry(top):
geom = top.wm_geometry() geom = top.wm_geometry()
m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom) m = re.match(r"(\d+)x(\d+)\+(-?\d+)\+(-?\d+)", geom)
if not m: return tuple(map(int, m.groups()))
top.bell()
return
width, height, x, y = map(int, m.groups())
newheight = top.winfo_screenheight()
# The constants below for Windows and Mac Aqua are visually determined
# to avoid taskbar or menubar and app icons. def set_window_geometry(top, geometry):
newy, bot_y = ((0, 72) if sys.platform == 'win32' else top.wm_geometry("{:d}x{:d}+{:d}+{:d}".format(*geometry))
(22, 88) if macosx.isAquaTk() else
(0, 88) ) # Guess for anything else.
newheight = newheight - newy - bot_y
newgeom = '' if height >= newheight else f"{width}x{newheight}+{x}+{newy}"
top.wm_geometry(newgeom)
return newgeom != ""
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -0,0 +1,4 @@
Adjust "Zoom Height" to individual screens by momemtarily maximizing the
window on first use with a particular screen. Changing screen settings
may invalidate the saved height. While a window is maximized,
"Zoom Height" has no effect.