gameboardviewer.rb

require 'Qt'

require './lifewidget.rb'

class GameBoardViewer < Qt::Widget

slots 'change_zoom(int)','change_x_offset(int)','change_y_offset(int)'

signals 'zoomChanged(int)'

attr_accessor :grid, :mode, :selection

def initialize(parent=nil)

super parent

@parent = parent

end

def init grid

@background_color = Qt::Color.new(0,0,255)

@piece_color = Qt::Color.new(0,255,0)

setPalette(Qt::Palette.new(@background_color))

setAutoFillBackground true

setMouseTracking true

@zoom = 50 #initial zoom level

@cell_size = (0.4*@zoom).floor.to_i

@offset_x = 0 # this is offset number of cells

@offset_y = 0

@ctrl_pressed = false

@mouse_button = Qt::NoButton

@last_mouse_pos = []

@mode = :none # :drawing, :erasing, :selecting

@select_pixels = []

@selection = []

@selection.extend Selection # add some functions to it, rotate/mirror, min/max etc

setFocusPolicy Qt::ClickFocus

setFocus

set_grid grid

setAcceptDrops true

end

def set_grid grid

@grid = grid

@grid_h = @grid.length

@grid_w = @grid[0].length

update

end

def change_zoom(zoom)

@zoom = zoom

@cell_size = (0.4*@zoom).floor.to_i

update

end

def change_x_offset(val)

@offset_x = val

update

end

def change_y_offset(val)

@offset_y = val

update

end

def resizeEvent event

end

def draw_grid painter

#vertical lines

for x in (0..(width/@cell_size))

painter.drawLine x*@cell_size,0,x*@cell_size,height

end

#horizontal lines

for y in (0..(height/@cell_size))

painter.drawLine 0, y*@cell_size,width,y*@cell_size

end

end

def paintEvent(event)

painter = Qt::Painter.new self

draw_grid painter

#draw the cells

viewport_x = (width/@cell_size).ceil

viewport_y = (height/@cell_size).ceil

for y in (@offset_y..(@offset_y+viewport_y))

for x in (@offset_x..(@offset_x+viewport_x))

color = @piece_color

if y >= @grid_h or x>=@grid_h

color = Qt::red

end

if @grid[y%@grid_h][x%@grid_w] == 1

rectx = (x-@offset_x)*@cell_size

recty = (y-@offset_y)*@cell_size

painter.fillRect rectx+1, recty+1, @cell_size-1, @cell_size-1, color

end

end

end

if @mode == :selecting # draw selection box

color = Qt::Color.new 10,100,100,150

brush = Qt::Brush.new color

w = @select_pixels[1][0] - @select_pixels[0][0]

h = @select_pixels[1][1] - @select_pixels[0][1]

painter.fillRect @select_pixels[0][0], @select_pixels[0][1], w, h, brush

elsif @mode == :copying or @mode == :moving # draw piece along with cursor

brush = Qt::Brush.new Qt::cyan

@selection.each do |pos|

x = @last_mouse_pos[0] + pos[0] - @offset_x

y = @last_mouse_pos[1] + pos[1] - @offset_y

painter.fillRect x*@cell_size+1, y*@cell_size+1, @cell_size-1, @cell_size-1, brush

end

end

painter.end

end

def win_to_grid winx,winy #converting gameboard pixel coordinates to grid coordianates

x = (winx / @cell_size + @offset_x) % @grid_w

y = (winy / @cell_size + @offset_y) % @grid_h

return x,y

end

def insert_selection # pastes selectio into the grid

@parent.push_grid if @parent.back_enabled

@selection.each do |pos|

@grid[pos[1]+@last_mouse_pos[1]][pos[0]+@last_mouse_pos[0]] = 1

end

update

end

def mousePressEvent event

@mouse_button = event.button

@ctrl_pressed = (event.modifiers!= Qt::NoModifier)

@last_mouse_pos = win_to_grid event.x, event.y #returns 2 element array

if @mouse_button == Qt::LeftButton

case @mode

when :copying

insert_selection

when :moving

insert_selection

else

if @ctrl_pressed and @mouse_button == Qt::LeftButton

@select_pixels[0] = [event.x,event.y]

@select_pixels[1] = [event.x,event.y]

@mode = :selecting

elsif event.button == Qt::LeftButton

@mode = :drawing

pos = win_to_grid event.x,event.y

@grid[pos[1]][pos[0]] = 1

end

update

end

elsif @mouse_button == Qt::RightButton

if @mode == :copying or @mode == :moving

#rotate piece

if @ctrl_pressed #left

@selection.rotate :left

else #right

@selection.rotate :right

end

update

else

@mode = :erasing

pos = win_to_grid event.x,event.y

@grid[pos[1]][pos[0]] = 0

update

end

elsif @mouse_button == Qt::MidButton

if @mode == :copying or @mode == :moving

# mirror the piece

if @ctrl_pressed

@selection.mirror :vertical

else

@selection.mirror :horizontal

end

update

else

@mode = :scrolling

end

end

update

end

def mouseMoveEvent event

grid_pos = win_to_grid event.x, event.y

case @mode

when :selecting

@last_mouse_pos = grid_pos

@select_pixels[1] = [event.x,event.y]

update

when :drawing

if grid_pos != @last_mouse_pos

@last_mouse_pos = grid_pos

@grid[grid_pos[1]][grid_pos[0]] = 1

update

end

when :erasing

if grid_pos != @last_mouse_pos

@last_mouse_pos = grid_pos

@grid[grid_pos[1]][grid_pos[0]] = 0

update

end

when :scrolling

if grid_pos != @last_mouse_pos

dx = @last_mouse_pos[0] - grid_pos[0]

dy = @last_mouse_pos[1] - grid_pos[1]

@offset_x += dx

@offset_x = 0 if @offset_x < 0

@offset_y += dy

@offset_y = 0 if @offset_y < 0

grid_pos[0] += dx

grid_pos[1] += dy

@last_mouse_pos = grid_pos

@parent.ui.horizontal_scrollbar.setValue @offset_x

@parent.ui.vertical_scrollbar.setValue @offset_y

update

end

when :copying

if grid_pos != @last_mouse_pos

@last_mouse_pos = grid_pos

update

end

when :moving

if grid_pos != @last_mouse_pos

@last_mouse_pos = grid_pos

update

end

end

@last_mouse_pos = grid_pos

end

def mouseReleaseEvent event

if event.button == Qt::LeftButton

case @mode

when :selecting

#make the selection

@mode = :none

# @select_pixels[0] is the upper left corner of selection box

# @select_pixels[1] is the lower right corner of selection box

xs = [@select_pixels[0][0], @select_pixels[1][0]]

select_width = xs.max - xs.min

ys = [@select_pixels[0][1],@select_pixels[1][1]]

select_height = ys.max - ys.min

x = (xs.min/@cell_size).ceil + @offset_x

y = (ys.min/@cell_size).ceil + @offset_y

w = (select_width/@cell_size).ceil + 1

h = (select_height/@cell_size).ceil + 1

@selection.clear

for yy in y...y+h

for xx in x..x+w

xx %= @grid_w

yy %= @grid_h

if @grid[yy][xx] == 1

@selection << [xx,yy]

end

end

end

when :drawing

@mode = :none

when :copying

when :moving

@mode = :none

update

else

@mode = :none

end

elsif event.button == Qt::RightButton

if @mode == :erasing

@mode = :none

end

elsif event.button == Qt::MiddleButton

if @mode != :copying and @mode != :moving

@mode = :none

end

end

@mouse_button = false

end

def keyPressEvent event

if event.key == Qt::Key_Escape

@mode = :none

update

elsif event.key == Qt::Key_C

#copy

@mode = :copying

@selection.adjust

update

elsif event.key == Qt::Key_X

#cut/move

@mode = :moving

@selection.each do |pos|

@grid[pos[1]][pos[0]] = 0

end

@selection.adjust

@selection.mouse_pos = @last_mouse_pos

update

elsif event.key == Qt::Key_D or event.key == Qt::Key_Delete

unless @selection.empty?

# delete selection

@selection.each do |elem|

@grid[elem[1]][elem[0]] = 0

end

update

end

else

event.ignore

end

end

def keyReleaseEvent event

@ctrl_pressed = false

end

def enterEvent event

setFocus(Qt::MouseFocusReason)

if $clipboard.has_item?

@selection.set deep_copy_matrix($clipboard.item)

@mode = :moving

$clipboard.item = nil

return

end

@mode = :none

end

def leaveEvent event

if @mode == :copying or @mode == :moving

selection = deep_copy_matrix @selection

$clipboard.item = selection

@selection.clear

update

end

end

def wheelEvent event

val = (event.delta/120).to_i

@zoom += val

emit zoomChanged(@zoom)

end

end

module Selection # adds some functions to a matrix to make code cleaner in a couple spots

def self.extended base

class << base

attr_accessor :mouse_pos

end

end

def set vals

clear

vals.each do |val|

self.<< val

end

end

def adjust #resets selection to start at 0,0

xmin = x_min #can't directly substitue x_max and y_max bellow because the following modifies the array

ymin = y_min

map!{|elem| [elem[0]-xmin,elem[1]-ymin]}

end

def rotate direction

if direction == :left

map!{ |elem| [-elem[1],elem[0]] }

else

map!{ |elem| [elem[1],-elem[0]] }

end

#adjust so there's no negative numbers

adjust

end

def mirror direction

if direction == :horizontal

xmax = x_max

map!{|elem| [xmax-elem[0],elem[1]]}

else

ymax = y_max

map!{|elem| [elem[0],ymax-elem[1]]}

end

end

def x_min

map{|elem| elem[0]}.min

end

def x_max

map{|elem| elem[0]}.max

end

def y_min

map{|elem| elem[1]}.min

end

def y_max

map{|elem| elem[1]}.max

end

end