A desktop environment lets users drag and resize windows by any edge, or by dragging the centre. Whatever they do, two rules always hold: a window is at least 100 pixels wide and 50 high, and it never leaves the screen. Screen coordinates start at the top-left corner (0, 0), with x growing to the right and y growing downwards.
Write a class Window(left, top, width, height, screen=(1920, 1080)) whose geometry is read and changed through attributes (no method calls):
- Moving (keeps the size): assigning
left,top, orcentre(a pair(x, y)). The window moves as asked, but stops at the screen's edges:leftends up between0andscreen_width - width, andtoplikewise. - Resizing (keeps the top-left corner): assigning
width,height,right(= left + width) orbottom(= top + height). The new width is kept between100andscreen_width - left, and the new height between50andscreen_height - top. - Reading: all of the above, plus
centre = (left + width // 2, top + height // 2),area = width * heightandbox = (left, top, right, bottom).areaandboxare read-only: assigning them raisesAttributeError. - The constructor first keeps the size between the minimum and the screen size, then places the window as a move would.
snap(where)fills the left half ("left": widthscreen_width // 2), the right half ("right": fromscreen_width // 2to the right edge) or the whole screen ("max"), always at full height.
The tests use the setup's desk(steps, *window_args): it builds Window(*window_args) and runs steps ("set", name, value), ("get", name) or ("snap", where), returning one result per step (None for a set or a snap, and the exception's name if one is raised).
Examples
Input: desk([("get", "box"), ("set", "left", 1900), ("get", "box"), ("set", "width", 50),
("get", "width"), ("set", "centre", (500, 20)), ("get", "box")], 100, 100, 400, 300)
Output: [(100, 100, 500, 400), None, (1520, 100, 1920, 400), None, 100, None,
(450, 0, 550, 300)]
Input: desk([("set", "right", 5000), ("get", "width"), ("set", "area", 1), ("snap", "right"), ("get", "box")], 10, 10, 200, 200)
Output: [None, 1910, 'AttributeError', None, (960, 0, 1920, 1080)]
Constraints
- All numbers are integers; screens are at least
100 x 50. - Up to
5000steps.
Goals
- Offer several views of the same state (edges, size, centre) as properties
- Keep an invariant true in every setter, whichever property is assigned
- Derive every value from a minimal stored state, so nothing goes stale