view src/net/kryshen/charamega/ui.mirah @ 1:fac1b8f35265

Applet.
author Mikhail Kryshen <mikhail@kryshen.net>
date Sat, 14 Jul 2012 06:32:31 +0400
parents 91ecd24948de
children 1e64a109812f
line wrap: on
line source

#
#  Copyright 2012 Mikhail Kryshen <mikhail@kryshen.net>
#
#  This file is part of Charamega.
#
#  Charamega is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  Charamega is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with Charamega.  If not, see <http://www.gnu.org/licenses/>.
#

package net.kryshen.charamega

import java.awt.*
import java.awt.event.*
import javax.swing.*
import java.net.URI

class Ui < JPanel

  def self.initialize:void
    @@title = 'Charamega'
    @@version = '0.9'
    @@home = 'http://kryshen.net/games/'
  end

  def initialize(root_pane:JRootPane)
    super LayoutManager(BorderLayout.new)

    @game = Game.new.shuffle(20)
    @field = Field.new(@game)
    @field.setBorder BorderFactory.createEmptyBorder(5, 5, 5, 5)

    add @field, BorderLayout.CENTER
    add create_status, BorderLayout.SOUTH

    ui = self
    @timer = Timer.new(250) { |e| ui.update }

    @conf = create_configurator
    root_pane.getLayeredPane.add @conf, JLayeredPane.MODAL_LAYER
    root_pane.setDefaultButton @start

    stop
    update_status
  end

  def doLayout
    @conf.setSize @conf.getPreferredSize
    @conf.setLocation int(getWidth * 0.05), int(getHeight * 0.05)
    super
  end

  def update
    @field.repaint  if @game.update
    stop  if @game.finished?
    update_status
  end

  def update_status
    @status.setText(@game.status)
  end

  def start
    @conf.setVisible false
    @field.setEnabled true
    @start.setEnabled false
    @stop.setEnabled true
    @game.start
    @timer.start
    @field.repaint
  end
  
  def stop
    @timer.stop
    @game.stop
    @stop.setEnabled false
    @start.setEnabled true
    @field.setEnabled false
    @conf.setVisible true
    @field.repaint
  end

  def self.create_frame
    title = @@title
    SwingUtilities.invokeLater do
      # UIManager.setLookAndFeel UIManager.getSystemLookAndFeelClassName
      
      frame = JFrame.new title
      ui = Ui.new(frame.getRootPane)
      
      frame.getContentPane.add ui
      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
        .setSize(800, 600)
        .validate
        .setVisible(true)
    end
  end

  private

  def create_status
    ui = self

    status_panel = JPanel.new
    layout = GroupLayout.new(status_panel)
    status_panel.setLayout layout

    @status = JLabel.new('', SwingConstants.RIGHT)
    @stop = JButton.new('New game')
    @stop.addActionListener do |e|
      ui.update_status
      ui.stop
    end
    
    gap = 5

    h_group = layout.createSequentialGroup
      .addGap(1, gap, gap)
      .addComponent(@stop)
      .addGap(1, gap, gap)
      .addComponent(@status, 
                    GroupLayout.DEFAULT_SIZE,
                    GroupLayout.DEFAULT_SIZE,
                    Short.MAX_VALUE)
      .addGap(1, gap, gap)

    v_group = layout.createSequentialGroup
      .addGap(1, gap, gap)
      .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
                  .addComponent(@stop)
                  .addComponent(@status))
      .addGap(1, gap, gap)
    
    layout.setHorizontalGroup h_group
    layout.setVerticalGroup v_group 

    status_panel
  end

  def create_configurator
    # Can't use object fields in closures in Mirah 0.11.
    ui = self
    game = @game
    field = @field

    conf = JPanel.new
    layout = GroupLayout.new(conf)
    conf.setLayout layout
    conf.setBorder BorderFactory.createRaisedBevelBorder

    layout.setAutoCreateGaps true
    layout.setAutoCreateContainerGaps true

    title = JLabel.new(@@title)
      .setFont(Font.new('serif', Font.BOLD, 26))

    version = JLabel.new("version #{@@version}")
    link = HyperlinkLabel.new(@@home)

    players_label = JLabel.new('Players: ')
    pairs_label = JLabel.new('Pairs: ')

    players = SpinnerNumberModel.new(1, 1, 20, 1)
    pairs = SpinnerNumberModel.new(20, 2, 200, 5)

    players_spinner = JSpinner.new(players)
    pairs_spinner = JSpinner.new(pairs)
    
    players.addChangeListener do |e|
      game.players = players.getNumber.intValue
      field.repaint  if game.shuffle
      ui.update_status
    end

    pairs.addChangeListener do |e|
      game.shuffle pairs.getNumber.intValue
      field.repaint
      ui.update_status
    end

    @start = JButton.new("Start")
    @start.addActionListener do |e|
      ui.start
    end

    h_group = layout.createParallelGroup
      .addGroup(layout.createSequentialGroup
                  .addComponent(title)
                  .addComponent(version))
      .addGroup(layout.createSequentialGroup
                  .addComponent(link,
                                GroupLayout.DEFAULT_SIZE,
                                GroupLayout.DEFAULT_SIZE,
                                GroupLayout.PREFERRED_SIZE)
                  .addGap(0, 0, Short.MAX_VALUE))
      .addGroup(layout.createSequentialGroup
                  .addGap(0, 0, Short.MAX_VALUE)
                  .addGroup(layout.createParallelGroup
                              .addComponent(pairs_label)
                              .addComponent(players_label))
                  .addGroup(layout.createParallelGroup
                              .addComponent(pairs_spinner)
                              .addComponent(players_spinner))
                  .addGap(0, 0, Short.MAX_VALUE))
      .addGroup(layout.createSequentialGroup
                  .addGap(0, 0, Short.MAX_VALUE)
                  .addComponent(@start))

    v_group = layout.createSequentialGroup
      .addGroup(layout.createBaselineGroup(false, true)
                  .addComponent(title)
                  .addComponent(version))
      .addComponent(link)
      .addGap(25)
      .addGroup(layout.createBaselineGroup(false, true)
                  .addComponent(pairs_label)
                  .addComponent(pairs_spinner))
      .addGroup(layout.createBaselineGroup(false, true)
                  .addComponent(players_label)
                  .addComponent(players_spinner))
      .addGap(15)
      .addComponent(@start)
    
    layout.setHorizontalGroup h_group
    layout.setVerticalGroup v_group 

    conf
  end
end

class HyperlinkLabel < JLabel

  def initialize(uri:String)
    initialize URI.new(uri), uri
  end

  def initialize(uri:URI)
    initialize uri, uri.toString
  end

  def initialize(uri:URI, text:String)
    @uri = uri

    s = text.replace('&', '&amp;')
      .replace('<', '&lt;')
      .replace('>', '&gt;')

    setText "<html><u>#{s}</u></html>"
    setForeground Color.BLUE.darker
    setCursor Cursor.new(Cursor.HAND_CURSOR)

    enableEvents AWTEvent.MOUSE_EVENT_MASK
  end

  def processMouseEvent(event)
    if event.getID == MouseEvent.MOUSE_CLICKED
      Desktop.getDesktop.browse @uri
    end

    super
  end
end

class Applet < JApplet
  def initialize
    getContentPane.add Ui.new(getRootPane)
  end
end

create_frame