// Windows.swift Copyright (c) Kari Laitinen // http://www.naturalprogramming.com // 2014-10-07 File created. // 2016-11-09 Last modification. /* This program displays simple character-based 'windows' to the Terminal screen. The classes of this program provide an example of a class hierarchy. Each class has at least one constructor. Constructors are commondly called initializers in Swift. */ import Foundation /* Class Window is the topmost class in the class hierarchy. A Window could look like //////////// //////////// //////////// //////////// In this window the width is 12, the height is 4, and the background character is '/' */ class Window { var MAXIMUM_WINDOW_WIDTH = 78 var MAXIMUM_WINDOW_HEIGHT = 22 // Window contents consist of characters that are stored // in a two-dimensional array. The characters are stored // as single-character Strings. var window_contents = Array>() var window_width : Int var window_height : Int var background_character = " " init() { window_width = MAXIMUM_WINDOW_WIDTH window_height = MAXIMUM_WINDOW_HEIGHT fill_with_character( background_character ) } init( _ desired_window_width : Int, _ desired_window_height : Int, _ given_background_character : String ) { window_width = desired_window_width window_height = desired_window_height background_character = given_background_character fill_with_character( background_character ) } func fill_with_character( _ filling_character : String ) { // This method creates the multidimensional array // that contains the window contents for _ in 1 ... window_width { var new_column_to_array = Array() for _ in 1 ... window_height { new_column_to_array.append( filling_character ) } window_contents.append( new_column_to_array ) } } func print_this_window() { print( "\n", terminator: "" ) for row_index in 0 ..< window_height { for column_index in 0 ..< window_width { print( window_contents[ column_index ] [ row_index ], terminator: "" ) } print( "\n", terminator: "" ) } } // The following method moves another window over 'this' window. func move( _ given_destination_x_index : Int, _ given_destination_y_index : Int, _ another_window : Window ) { // Swift no longer accepts function parameters to be used // as variables. Therefore, we copy the given parameters // to local variables. var destination_x_index = given_destination_x_index var destination_y_index = given_destination_y_index var source_y_index = 0 while source_y_index < another_window.window_height { if destination_y_index >= 0 && destination_y_index < window_height { var source_x_index = 0 let saved_destination_x_index = destination_x_index while source_x_index < another_window.window_width { if destination_x_index >= 0 && destination_x_index < window_width { window_contents [ destination_x_index ][ destination_y_index ] = another_window.window_contents[ source_x_index ] [ source_y_index ] } source_x_index += 1 destination_x_index += 1 } destination_x_index = saved_destination_x_index } source_y_index += 1 destination_y_index += 1 } } } /* Class FrameWindow represents a window that looks like |----------------------| | | | | | | | | | | |----------------------| The window has frames that are made by using characters '|' and '-'. The inner area of the window contains space characters. */ class FrameWindow : Window { init( _ desired_window_width : Int, _ desired_window_height : Int ) { super.init( desired_window_width, desired_window_height, "|" ) let horizontal_frames = Window( window_width - 2, window_height, "-" ) let spaces_inside_window = Window( window_width - 2, window_height - 2, " " ) move( 1, 0, horizontal_frames ) move( 1, 1, spaces_inside_window ) } // Convenience initializers are used to call other // initializers (constructors) of the same class. convenience override init() { self.init( 40, 10 ) ; // Calling the other initializer above. } } /* A TextWindow can look like the following |----------------------------| | | | | | | | Hello, world. | | | | | |----------------------------| */ class TextWindow : FrameWindow { var text_inside_window : String = "" func embed_text_in_window() { let text_length = text_inside_window.characters.count let text_row = window_height / 2 let text_start_column = (window_width - text_length) / 2 for character_index in 0 ..< text_length { let character_column = text_start_column + character_index let actual_index = text_inside_window.index( text_inside_window.startIndex, offsetBy: character_index ) window_contents[ character_column ] [ text_row ] = String( text_inside_window[ actual_index ] ) } } init( _ desired_window_width : Int, _ desired_window_height : Int, _ given_line_of_text : String ) { super.init( desired_window_width, desired_window_height ) text_inside_window = given_line_of_text embed_text_in_window() } func change_text( _ new_line_of_text : String ) { text_inside_window = new_line_of_text embed_text_in_window() } } /* The lowest class in the hierarchy is DecoratedTextWindow. Such a window has a decoration frame that is made with characters '*': **************************** *|------------------------|* *| |* *| |* *| |* *| Smile! |* *| |* *| |* *| |* *|------------------------|* **************************** */ class DecoratedTextWindow : TextWindow { override init( _ desired_window_width : Int, _ desired_window_height : Int, _ given_line_of_text : String ) { super.init( desired_window_width, desired_window_height, given_line_of_text ) let decoration_frame = Window( desired_window_width, desired_window_height, "*" ) let window_inside_window = TextWindow( desired_window_width - 2, desired_window_height - 2, given_line_of_text ) decoration_frame.move( 1, 1, window_inside_window ) move( 0, 0, decoration_frame ) } } var background_window = Window( 76, 22, "/" ) var empty_window = FrameWindow( 24, 7 ) var greeting_window = TextWindow( 30, 8, "Hello, world." ) var smiling_window = DecoratedTextWindow( 28, 11, "Smile!" ) background_window.move( 6, 2, empty_window ) background_window.move( 4, 12, greeting_window ) greeting_window.change_text( "HELLO, UNIVERSE!" ) background_window.move( 43, 11, greeting_window ) background_window.move( 40, 3, smiling_window ) background_window.print_this_window()