[fxruby-users] non-modal dialogs

Joel VanderWerf vjoel at PATH.Berkeley.EDU
Wed Dec 15 04:23:13 EST 2004


I had to convert some modal dialogs to non-modal, and I abstracted out 
some of what I was doing. The result is a mixin that uses closures to 
make non-modal dialogs almost as easy to implement as modal ones. It's 
going into the next release of FoxTails, but since it stands alone and 
is quite simple, I'm submitting it here. Hope it helps someone.

The key to using this mixin is in the initialization of the main window:

     nonModalButton.connect(SEL_COMMAND) do
       @dialog.execute_nonmodal do |accepted|
         @label.text = accepted ? "Dialog accepted" : "Dialog cancelled"
       end
     end

The code block is saved away and called later when the dialog is exited. 
But since it's closure, you can operate on local vars, etc. to apply the 
result of the dialog. Normally, the way non-modal dialogs interact with 
their callers is much more awkward than in the modal case, AFAICT.
-------------- next part --------------
require 'fox'
require 'fox/responder'

include Fox

module FoxTails
  # Module to include in FXDialogBox to provide an easy nonmodal version
  # of execute.
  module FTNonModal
    include Responder

    def initialize(*args)
      super if defined?(super)
      FXMAPFUNC(SEL_COMMAND, FXDialogBox::ID_CANCEL, :onCmdCancel)
      FXMAPFUNC(SEL_COMMAND, FXDialogBox::ID_ACCEPT, :onCmdAccept)
    end

    # Creates and shows the dialog, and registers the associated block to be
    # called when the dialog is closed. The block is passed a boolean argument
    # which is true if and only if the dialog was accepted.
    def execute_nonmodal(placement = PLACEMENT_CURSOR, &block)
      @__FTNonModal_block = block
      create
      show placement
   end

    def onCmdCancel(*args) # :nodoc:
      on_nonmodal_close(false)
    end

    def onCmdAccept(*args) # :nodoc:
      on_nonmodal_close(true)
    end

    # Called when dialog is closed, with _accepted_ equal to true if and only if
    # the user accepted the dialog.
    def on_nonmodal_close(accepted)
      @__FTNonModal_block[accepted]

      ##return 0 -- why isn't this enough to close window?
      ## oh well, let's immitate FXTopWindow:
      getApp().stopModal(self, accepted ? 1 : 0)
      hide()
    end
  end
end

class TestDialog < FXDialogBox
  include FoxTails::FTNonModal
  
  def initialize(*args)
    super
    
    buttons = FXHorizontalFrame.new(self,
      LAYOUT_SIDE_BOTTOM|FRAME_NONE|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH)

    FXButton.new(buttons, "&Accept", nil, self, ID_ACCEPT,
      FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)
  
    FXButton.new(buttons, "&Cancel", nil, self, ID_CANCEL,
      FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)
  end
end

class MainWindow < FXMainWindow
  def initialize(app)
    super(app, "NonModal test", nil, nil, DECOR_ALL)
    
    frame = FXHorizontalFrame.new(self)

    nonModalButton = FXButton.new(frame, "&Non-Modal Dialog...", nil, nil, 0,
      FRAME_RAISED|FRAME_THICK|LAYOUT_CENTER_X|LAYOUT_CENTER_Y)
    
    nonModalButton.connect(SEL_COMMAND) do
      @dialog.execute_nonmodal do |accepted|
        @label.text = accepted ? "Dialog accepted" : "Dialog cancelled"
      end
    end
  
    @label = FXLabel.new(frame, "Dialog not invoked yet")
    
    @dialog = TestDialog.new(getApp(), "NonModal Dialog",
      DECOR_TITLE|DECOR_BORDER)
    
    show
  end

end

if __FILE__ == $0
  application = FXApp.new("NonModal", "FoxTailsTest")
  window = MainWindow.new(application)
  application.create
  application.run
end


More information about the fxruby-users mailing list