Layout & Positioning
CocoaGUI uses absolute positioning with x and y coordinates. While this is simpler than complex layout systems, it requires some planning to create organized interfaces.
Understanding Coordinates
CocoaGUI positions widgets using pixel coordinates from the top-left corner:
- X increases moving right
- Y increases moving down
- Top-left of window is (0, 0)
Basic Positioning
import CocoaGUI as gui
app = gui.Window("Position Demo", width=400, height=300)
# Top-left corner
gui.Label(app, "Top Left", x=10, y=10)
# Top-right area
gui.Label(app, "Top Right", x=310, y=10)
# Bottom-left area
gui.Label(app, "Bottom Left", x=10, y=270)
# Center area
gui.Label(app, "Center", x=175, y=135)
app.run()
Layout Patterns
Vertical Stack
Place widgets in a vertical column:
import CocoaGUI as gui
app = gui.Window("Vertical Stack", width=400, height=350)
x = 50 # Fixed x position
y = 20 # Starting y position
spacing = 40 # Space between items
gui.Label(app, "Name:", x=x, y=y)
gui.Input(app, x=x, y=y+25, width=300)
y += spacing + 25 # Move down
gui.Label(app, "Email:", x=x, y=y)
gui.Input(app, x=x, y=y+25, width=300)
y += spacing + 25
gui.Label(app, "Message:", x=x, y=y)
gui.TextArea(app, x=x, y=y+25, width=300, height=100)
app.run()
Horizontal Row
Place widgets side-by-side:
import CocoaGUI as gui
app = gui.Window("Horizontal Row", width=500, height=200)
y = 80 # Fixed y position
x = 50 # Starting x position
spacing = 80 # Space between buttons
gui.Button(app, "New", command=None, x=x, y=y)
x += spacing
gui.Button(app, "Open", command=None, x=x, y=y)
x += spacing
gui.Button(app, "Save", command=None, x=x, y=y)
x += spacing
gui.Button(app, "Exit", command=None, x=x, y=y)
app.run()
Grid Layout
Create a grid of widgets:
import CocoaGUI as gui
app = gui.Window("Grid Layout", width=450, height=400)
start_x = 50
start_y = 50
cell_width = 120
cell_height = 80
# Create a 3x3 grid of buttons
for row in range(3):
for col in range(3):
x = start_x + (col * cell_width)
y = start_y + (row * cell_height)
label = f"R{row+1}C{col+1}"
gui.Button(app, label, command=None, x=x, y=y)
app.run()
Form Layout
Labels on the left, inputs on the right:
import CocoaGUI as gui
app = gui.Window("Form Layout", width=450, height=400)
label_x = 50
input_x = 150
start_y = 50
row_height = 60
# Row 1
gui.Label(app, "First Name:", x=label_x, y=start_y)
gui.Input(app, x=input_x, y=start_y-3, width=250)
# Row 2
gui.Label(app, "Last Name:", x=label_x, y=start_y + row_height)
gui.Input(app, x=input_x, y=start_y + row_height - 3, width=250)
# Row 3
gui.Label(app, "Email:", x=label_x, y=start_y + row_height*2)
gui.Input(app, x=input_x, y=start_y + row_height*2 - 3, width=250)
# Row 4
gui.Label(app, "Phone:", x=label_x, y=start_y + row_height*3)
gui.Input(app, x=input_x, y=start_y + row_height*3 - 3, width=250)
# Submit button centered below form
gui.Button(app, "Submit", command=None, x=180, y=start_y + row_height*4 + 20)
app.run()
Sidebar Layout
Sidebar on the left, main content on the right:
import CocoaGUI as gui
app = gui.Window("Sidebar Layout", width=700, height=500)
sidebar_width = 150
sidebar_x = 20
content_x = sidebar_width + 40
# Sidebar title
gui.Label(app, "Menu", x=sidebar_x + 40, y=20, size=14)
# Sidebar buttons (vertical stack)
button_y = 60
for label in ["Home", "Profile", "Settings", "Help", "Exit"]:
gui.Button(app, label, command=None, x=sidebar_x, y=button_y)
button_y += 50
# Content area
gui.Label(app, "Main Content Area", x=content_x, y=20, size=16)
gui.TextArea(app, x=content_x, y=60, width=480, height=400)
app.run()
Header-Body-Footer Layout
import CocoaGUI as gui
app = gui.Window("Three Section Layout", width=600, height=500)
# Header
header_y = 20
gui.Label(app, "Application Title", x=220, y=header_y, size=18)
# Body
body_y = 70
gui.TextArea(app, x=20, y=body_y, width=560, height=350)
# Footer
footer_y = 440
gui.Label(app, "Status: Ready", x=20, y=footer_y, size=10)
gui.Button(app, "Action", command=None, x=480, y=footer_y-5)
app.run()
Centering Widgets
Horizontal Centering
Calculate the center position:
window_width = 400
widget_width = 200
# Center horizontally
x = (window_width - widget_width) // 2
gui.Input(app, x=x, y=100, width=widget_width)
Center a Button
window_width = 400
button_width = 60 # Approximate button width
x = (window_width - button_width) // 2
gui.Button(app, "Click", command=None, x=x, y=150)
Center Text Label
Text centering requires estimating text width:
text = "Hello, World!"
font_size = 14
# Rough estimate: 7-8 pixels per character at size 12-14
char_width = 8
text_width = len(text) * char_width
window_width = 400
x = (window_width - text_width) // 2
gui.Label(app, text, x=x, y=100, size=font_size)
Spacing Guidelines
Recommended Spacing
- Margin from window edge: 10-20 pixels
- Between related widgets: 5-10 pixels
- Between widget groups: 20-30 pixels
- Between rows in forms: 40-60 pixels
Example with Good Spacing
import CocoaGUI as gui
app = gui.Window("Good Spacing", width=450, height=350)
margin = 20
group_spacing = 30
item_spacing = 40
y = margin
# Group 1: Personal Info
gui.Label(app, "Personal Information", x=margin, y=y, size=14)
y += group_spacing
gui.Label(app, "Name:", x=margin, y=y)
gui.Input(app, x=margin + 100, y=y, width=300)
y += item_spacing
gui.Label(app, "Age:", x=margin, y=y)
gui.Input(app, x=margin + 100, y=y, width=100)
y += item_spacing + group_spacing
# Group 2: Contact Info
gui.Label(app, "Contact Information", x=margin, y=y, size=14)
y += group_spacing
gui.Label(app, "Email:", x=margin, y=y)
gui.Input(app, x=margin + 100, y=y, width=300)
y += item_spacing
gui.Label(app, "Phone:", x=margin, y=y)
gui.Input(app, x=margin + 100, y=y, width=300)
app.run()
Responsive Design Workarounds
CocoaGUI uses fixed positioning, but you can make layouts more flexible:
Calculate Positions from Window Size
import CocoaGUI as gui
width = 600
height = 400
app = gui.Window("Flexible Layout", width=width, height=height)
# Calculate positions based on window size
margin = 20
center_x = width // 2
center_y = height // 2
# Center a button
button_width = 80
button_x = center_x - (button_width // 2)
gui.Button(app, "Centered", command=None, x=button_x, y=center_y)
# Bottom-right button
gui.Button(app, "Exit", command=None, x=width-100, y=height-60)
app.run()
Use Variables for Maintainability
import CocoaGUI as gui
app = gui.Window("Maintainable Layout", width=500, height=400)
# Define layout constants
MARGIN = 20
LABEL_WIDTH = 100
INPUT_WIDTH = 350
ROW_HEIGHT = 50
# Calculate x positions
label_x = MARGIN
input_x = MARGIN + LABEL_WIDTH + 10
# Calculate y positions
y = MARGIN
for field in ["Name", "Email", "Address", "City", "Country"]:
gui.Label(app, f"{field}:", x=label_x, y=y)
gui.Input(app, x=input_x, y=y, width=INPUT_WIDTH)
y += ROW_HEIGHT
app.run()
Common Layout Mistakes
Too Close to Edges
# ❌ Bad: No margin
gui.Label(app, "Text", x=0, y=0)
# ✅ Good: Proper margin
gui.Label(app, "Text", x=20, y=20)
Inconsistent Spacing
# ❌ Bad: Random spacing
gui.Label(app, "Name:", x=50, y=40)
gui.Input(app, x=120, y=38, width=200)
gui.Label(app, "Email:", x=50, y=95) # Random jump
gui.Input(app, x=125, y=92, width=200) # Different offset
# ✅ Good: Consistent spacing
spacing = 50
y = 40
gui.Label(app, "Name:", x=50, y=y)
gui.Input(app, x=120, y=y, width=200)
y += spacing
gui.Label(app, "Email:", x=50, y=y)
gui.Input(app, x=120, y=y, width=200)
Hardcoded Positions
# ❌ Bad: Magic numbers everywhere
gui.Button(app, "OK", command=None, x=237, y=418)
# ✅ Good: Calculated positions
window_width = 500
window_height = 450
button_width = 60
x = (window_width - button_width) // 2
y = window_height - 50
gui.Button(app, "OK", command=None, x=x, y=y)
Layout Planning Tips
- Sketch first: Draw your layout on paper before coding
- Use constants: Define margins, spacing as variables
- Group related widgets: Keep related items close together
- Leave breathing room: Don't cram everything together
- Test different window sizes: Make sure it looks good
- Align elements: Line up widgets for visual clarity
- Use consistent spacing: Same spacing for similar elements
Advanced Techniques
Dynamic Positioning
Create widgets in loops with calculated positions:
import CocoaGUI as gui
app = gui.Window("Dynamic Layout", width=600, height=400)
items = ["Home", "About", "Services", "Products", "Contact"]
start_x = 50
y = 100
spacing = 100
for i, item in enumerate(items):
x = start_x + (i * spacing)
gui.Button(app, item, command=None, x=x, y=y)
app.run()
Reusable Layout Functions
def create_form_row(app, label_text, y_position):
"""Create a label-input row"""
gui.Label(app, label_text, x=50, y=y_position)
input_field = gui.Input(app, x=150, y=y_position, width=300)
return input_field
# Use it
name = create_form_row(app, "Name:", 50)
email = create_form_row(app, "Email:", 100)
phone = create_form_row(app, "Phone:", 150)
See Also
- Best Practices - General UI design tips
- Window API - Window configuration
- Examples - Real-world layouts