Interactive
Software Engineering
Windows Eiffel Library (WEL) Tutorial

[ISE Home] Home ] Release Notes ] Technology Papers ] Installation Notes ] About Eiffel ]


Step 6: Repainting a window

As you have probably noticed, the graphics and text you draw in a window using device context functions (like line_to or text_out) disappear when you resize or uncover the window. Windows does not save the graphics that you draw in the device context, the application is in charge to refresh the window when it is necessary. In this step you will learn how to do that.

When the user of your application resizes or uncovers a window, it requires updating, or painting. WEL automatically calls the on_paint procedure (from WEL_COMPOSITE_WINDOW) when the window needs to be painted. Procedure on_paint is where you write the code to paint the contents of the window. There is one major difference between drawing graphics in the on_paint procedure and at other times, such as in response to mouse actions. The device context to be used for painting is passed in the paint_dc parameter, so your program does not need to get and release it. You will, however, need to select your drawing tools into the paint_dc.

To paint a window's contents, you are going to replay the actions that led to the original drawing on dc, but use paint_dc instead. But first, you need to store the graphic as objects, so you can paint them in the on_paint procedure.

Let's say that the window's contents is a set of lines, and each line is a set of points with a width. Then, you can simply define a line as follows:

class 
	LINE
inherit 
	LINKED_LIST [POINT]
creation  
	make

feature   -- Access

	width: INTEGER
			-- Width of the line
			
feature   -- Element change

	set_width (a_width: INTEGER) is 
			-- Set width with a_width.
		require 
			positive_width: a_width >= 0
		do 
			width := a_width
		ensure 
			width_set: width = a_width
		end
	
	add (x, y: INTEGER) is 
			-- Add a point specified by x and y.
		local 
			p: POINT
		do 
			create p.make (x, y)
			extend (p)
		end
	
invariant 
	positive_width: width >= 0
	
end  -- class LINE

Class POINT is simply defined as follows:

class 
	POINT

creation
	make

feature   -- Initialization

	make (a_x, a_y: INTEGER) is 
			-- Make a point with a_x and a_y.
		do 
			x := a_x
			y := a_y
		ensure 
			x_set: x = a_x
			y_set: y = a_y
		end 

feature   -- Access

	x: INTEGER
			-- x position
	y: INTEGER
			-- y position
end  -- class POINT

Using class LINE, the basic idea consists of saving mouse movements while the user draws. Then, you will use these data in the on_paint procedure to redraw window's contents. First, you need to add the following attributes in class MAIN_WINDOW.

	lines: LINKED_LIST [LINE]
			-- All lines drawn by the user
			
	current_line: LINE
			-- Line currently drawn by the user

Attribute lines needs to be created in the make routine as follows:

	make is 
			-- Make the main window.
		do 
			make_top ("My application")
			create dc.make (Current)
			set_pen_width (1)
			create lines.make
		end

And finally, you have to change on_left_button_down and on_mouse_move to store the points in lines.

	on_left_button_down (keys, x_pos, y_pos: INTEGER) is 
			-- Initiate the drawing process.
		do 
			if  not  button_down then 
				button_down := true
				dc.get
				dc.move_to (x_pos, y_pos)
				dc.select_pen (pen)
				create current_line.make
				current_line.set_width (pen.width)
				lines.extend (current_line)
				current_line.add (x_pos, y_pos)
			end 
		end
	
	on_mouse_move (keys, x_pos, y_pos: INTEGER) is 
			-- Connect the points to make lines.
		do 
			if  button_down then 
				dc.line_to (x_pos, y_pos)
				current_line.add (x_pos, y_pos)
			end 
		end

At this point, lines has all the information needed to redraw the window's contents. Basically, you just need to redefine on_paint and iterate over the list to draw the lines as follows:

	on_paint (paint_dc: WEL_PAINT_DC; invalid_rect: WEL_RECT) is 
			-- Paint the lines.
		local 
			a_line: LINE
			a_pen: WEL_PEN
			first_point: BOOLEAN
		do 
			from 
				lines.start
			until 
				lines.off
			loop 
				from 
					first_point := true
					a_line := lines.item
					a_line.start
					create a_pen.make_solid (a_line.width, black)
					paint_dc.select_pen (a_pen)
				until 
					a_line.off
				loop 
					if  first_point then 
						first_point := false
						paint_dc.move_to (a_line.item.x, a_line.item.y)
					else 
						paint_dc.line_to (a_line.item.x, a_line.item.y)
					end
					a_line.forth
				end
				lines.forth
			end 
		end

Now, if you minimize and restore the window, you will see that window's contents is restored.

Here is the full text of MAIN_WINDOW (Professional version):

class 
	MAIN_WINDOW
inherit 
	WEL_FRAME_WINDOW
		redefine 
			on_left_button_down, on_left_button_up, 
			on_right_button_down, on_mouse_move, 
			on_paint, closeable
		end
	WEL_STANDARD_COLORS
		export 
			{NONE} all 
		end 

creation  
	make

feature  {NONE} -- Initialization

	make is 
			-- Make the main window.
		do 
			make_top ("My application")
			create dc.make (Current)
			set_pen_width (1)
			create lines.make
		end
	
feature   -- Access

	dc: WEL_CLIENT_DC
			-- Device context associated to the current
			-- client window
			
	button_down: BOOLEAN
			-- Is the left mouse button down?
			
	pen: WEL_PEN
			-- Pen currently selected in dc
			
	line_thickness_dialog: LINE_THICKNESS_DIALOG
			-- Dialog box to change line thickness
			
	lines: LINKED_LIST [LINE]
			-- All lines drawn by the user
			
	current_line: LINE
			-- Line currently drawn by the user
			
feature   -- Element change

	set_pen_width (new_width: INTEGER) is 
			-- Set pen width with new_width.
		do 
			create pen.make_solid (new_width, black)
		end
	
feature  {NONE} -- Implementation

	on_left_button_down (keys, x_pos, y_pos: INTEGER) is 
			-- Initiate the drawing process.
		do 
			if  not  button_down then 
				button_down := true
				dc.get
				dc.move_to (x_pos, y_pos)
				dc.select_pen (pen) create current_line.make
				current_line.set_width (pen.width)
				lines.extend (current_line)
				current_line.add (x_pos, y_pos)
			end 
		end

	on_mouse_move (keys, x_pos, y_pos: INTEGER) is
			-- Connect the points to make lines.
		do 
			if  button_down then 
				dc.line_to (x_pos, y_pos)
				current_line.add (x_pos, y_pos)
			end 
		end

	on_left_button_up (keys, x_pos, y_pos: INTEGER) is 
			-- Terminate the drawing process.
		do 
			if  button_down then 
				button_down := false
				dc.release
			end 
		end

	on_right_button_down (keys, x_pos, y_pos: INTEGER) is 
			-- Bring up line_thickness_dialog and set the -- new pen width.
		do 
			if  line_thickness_dialog = void then 
				create line_thickness_dialog.make (Current)
			end
			line_thickness_dialog.activate
			if  line_thickness_dialog.ok_pushed then 
				set_pen_width (line_thickness_dialog.pen_width)
			end 
		end

	on_paint (paint_dc: WEL_PAINT_DC; invalid_rect: WEL_RECT) is 
			-- Paint the lines.
		local 
			a_line: LINE
			a_pen: WEL_PEN
			first_point: BOOLEAN
		do 
			from 
				lines.start
			until 
				lines.off
			loop 
				from 
					first_point := true
					a_line := lines.item
					a_line.start
					create a_pen.make_solid (a_line.width, black)
					paint_dc.select_pen (a_pen)
				until 
					a_line.off
				loop 
					if  first_point then 
						first_point := false
						paint_dc.move_to (a_line.item.x, a_line.item.y)
					else 
						paint_dc.line_to (a_line.item.x, a_line.item.y)
					end
					a_line.forth
				end
				lines.forth
			end 
		end

	closeable: BOOLEAN is 
			-- Does the user want to quit?
		local
			msgBox: WEL_MSG_BOX
		do
			create msgBox.make
			msgBox.question_message_box (Current, "Do you want to quit?", "Quit")	
			Result := msgBox.message_box_result = Mb_ok
		end

end  -- class MAIN_WINDOW
Here is the full text of MAIN_WINDOW (Personal version):
class 
	MAIN_WINDOW
inherit 
	WEL_FRAME_WINDOW
		redefine 
			on_left_button_down, on_left_button_up,
			on_right_button_down, on_mouse_move,
			on_paint, closeable
		end
	WEL_STANDARD_COLORS
		export 
			{NONE} all 
		end

creation  
	make

feature  {NONE} -- Initialization

	make is 
			-- Make the main window.
		do 
			make_top ("My application")
			create dc.make (Current)
			set_pen_width (1)
			create lines.make
		end
	
feature   -- Access

	dc: WEL_CLIENT_DC
			-- Device context associated to the current
			-- client window
			
	button_down: BOOLEAN
			-- Is the left mouse button down?
			
	pen: WEL_PEN
			-- Pen currently selected in dc
			
	line_thickness_window: LINE_THICKNESS_WINDOW
			-- Window to change line thickness
			
	lines: LINKED_LIST [LINE]
			-- All lines drawn by the user
			
	current_line: LINE
			-- Line currently drawn by the user
			
feature   -- Element change

	set_pen_width (new_width: INTEGER) is 
			-- Set pen width with new_width.
		do 
			create pen.make_solid (new_width, black)
		end
	
feature  {NONE} -- Implementation

	on_left_button_down (keys, x_pos, y_pos: INTEGER) is 
			-- Initiate the drawing process.
		do 
			if  not  button_down then 
				button_down := true 
				dc.get
				dc.move_to (x_pos, y_pos)
				dc.select_pen (pen)
				create current_line.make
				current_line.set_width (pen.width)
				lines.extend (current_line)
				current_line.add (x_pos, y_pos)
			end 
		end
	
	on_mouse_move (keys, x_pos, y_pos: INTEGER) is 
			-- Connect the points to make lines.
		do 
			if  button_down then 
				dc.line_to (x_pos, y_pos)
				current_line.add (x_pos, y_pos)
			end 
		end
	
	on_left_button_up (keys, x_pos, y_pos: INTEGER) is 
			-- Terminate the drawing process.
		do 
			if  button_down then 
				button_down := false
				dc.release
			end 
		end
	
	on_right_button_down (keys, x_pos, y_pos: INTEGER) is 
			-- Bring up line_thickness_window and set the
			-- new pen width.
		do 
			if  line_thickness_window = void then 
				create line_thickness_window.make (Current)
			end
			line_thickness_window.activate
		end
	
	on_paint (paint_dc: WEL_PAINT_DC; invalid_rect: WEL_RECT) is 
			-- Paint the lines.
		local 
			a_line: LINE
			a_pen: WEL_PEN
			first_point: BOOLEAN
		do 
			from 
				lines.start
			until 
				lines.off
			loop 
				from 
					first_point := true 
					a_line := lines.item
					a_line.start
					create a_pen.make_solid (a_line.width, black)
					paint_dc.select_pen (a_pen)
				until 
					a_line.off
				loop 
					if  first_point then 
						first_point := false 
						paint_dc.move_to (a_line.item.x, a_line.item.y)
					else 
						paint_dc.line_to (a_line.item.x, a_line.item.y)
					end
					a_line.forth
				end
				lines.forth
			end 
		end
	
	closeable: BOOLEAN is 
			-- Does the user want to quit?
		local
			msgBox: WEL_MSG_BOX
		do
			create msgBox.make
			msgBox.question_message_box (Current, "Do you want to quit?", "Quit")	
			Result := msgBox.message_box_result = Mb_ok
		end
	
end  -- class MAIN_WINDOW

Previous Table of Contents Next