Class: Io::PromptMenu

Inherits:
Object show all
Includes:
Io
Defined in:
lib/rbcurse/core/include/io.rb

Overview

An encapsulated form of yesterday’s Most Menu It keeps the internals away from the user. Its not really OOP in the sense that the PromptMenu is not a MenuItem. That’s how it is in our Menu system, and that led to a lot of painful coding (at least for me). This is quite simple. A submenu contains a PromptMenu in its action object and is evaluated in a switch. A recursive loop handles submenus.

Prompting of menu options with suboptions etc. A block of code or symbol or proc is executed for any leaf node This allows us to define different menus for different objects on the screen, and not have to map all kinds of control keys for operations, and have the user remember them. Only one key invokes the menu and the rest are ordinary characters.

== Example
  menu = PromptMenu.new self do
    item :s, :goto_start
    item :b, :goto_bottom
    item :r, :scroll_backward
    item :l, :scroll_forward
    submenu :m, "submenu" do
      item :p, :goto_last_position
      item :r, :scroll_backward
      item :l, :scroll_forward
    end
  end
  menu.display @form.window, $error_message_row, $error_message_col, $datacolor #, menu

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Io

#__create_footer_window, #clear_this, #get_file, #print_this, #rb_getchar, #rb_gets, #rbgetstr, #warn

Constructor Details

#initialize(caller, text = "Choose:", &block) ⇒ PromptMenu

Returns a new instance of PromptMenu.



313
314
315
316
317
318
# File 'lib/rbcurse/core/include/io.rb', line 313

def initialize caller,  text="Choose:", &block
  @caller = caller
  @text = text
  @options = []
  yield_or_eval &block if block_given?
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



312
313
314
# File 'lib/rbcurse/core/include/io.rb', line 312

def options
  @options
end

#textObject (readonly)

Returns the value of attribute text.



311
312
313
# File 'lib/rbcurse/core/include/io.rb', line 311

def text
  @text
end

Class Method Details

.create_menuitem(*args) ⇒ Object

Added this, since actually it could have been like this 2011-12-22



351
352
353
# File 'lib/rbcurse/core/include/io.rb', line 351

def self.create_menuitem *args
  item = CMenuItem.new(*args.flatten)
end

Instance Method Details

#add(*menuitem) ⇒ Object Also known as: item



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/rbcurse/core/include/io.rb', line 319

def add *menuitem
  item = nil
  case menuitem.first
  when CMenuItem
    item = menuitem.first
    @options << item
  else
    case menuitem.size
    when 4
      item = CMenuItem.new(*menuitem.flatten)
    when 2
      # if user only sends key and symbol
      menuitem[3] = menuitem[1]
      item = CMenuItem.new(*menuitem.flatten)
    when 1
      if menuitem.first.is_a? Action
        item = menuitem.first
      else
        raise ArgumentError, "Don't know how to handle #{menuitem.size} : #{menuitem} "
      end
    else
      raise ArgumentError, "Don't know how to handle #{menuitem.size} : #{menuitem} "
    end
    @options << item
  end
  return item
end

#create_mitem(*args) ⇒ Object



347
348
349
# File 'lib/rbcurse/core/include/io.rb', line 347

def create_mitem *args
  item = CMenuItem.new(*args.flatten)
end

#display(win, r, c, color) ⇒ Object

Display the top level menu and accept user input Calls actions or symbols upon selection, or traverses submenus and is not restricted to one row. Avoid this.

Parameters:

  • win

    window

  • r,

    c row and col to display on

  • color

    text color (use $datacolor if in doubt)

Returns:

  • retvalue of last call or send, or 0

See Also:



484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/rbcurse/core/include/io.rb', line 484

def display win, r, c, color
  raise "Please use display_new, i've replace this with that"
  # FIXME use a oneline window, user should not have to give all this crap.
  # What about panning if we can;t fit, should we use horiz list to show ?
  menu = @options
  $log.debug " DISP MENU "
  ret = 0
  while true
    str = @text.dup
    h = {}
    valid = []
    menu.each{ |item|
      hk = item.hotkey.to_s
      str << "(%c) %s " % [ hk, item.label ]
      h[hk] = item
      valid << hk
    }
    #$log.debug " valid are #{valid} "
    color = $datacolor
    print_this(win, str, color, r, c)
    ch=win.getchar()
    #$log.debug " got ch #{ch} "
    next if ch < 0 or ch > 255
    if ch == 3 || ch == ?\C-g.getbyte(0)
      clear_this win, r, c, color, str.length
      print_this(win, "Aborted.", color, r,c)
      break
    end
    ch = ch.chr
    index = valid.index ch
    if index.nil?
      clear_this win, r, c, color, str.length
      print_this(win, "Not valid. Valid are #{valid}", color, r,c)
      sleep 1
      next
    end
    #$log.debug " index is #{index} "
    item = h[ch]
    desc = item.desc
    #desc ||= "Could not find desc for #{ch} "
    desc ||= ""
    clear_this win, r, c, color, str.length
    print_this(win, desc, color, r,c)
    action = item.action
    case action
      #when Array
    when PromptMenu
      # submenu
      menu = action.options
      str = "%s: " % action.text 
    when Proc
      ret = action.call
      break
    when Symbol
      ret = @caller.send(action)
      break
    else 
      $log.debug " Unidentified flying class #{action.class} "
      break
    end
  end # while
  return ret # ret val of last send or call
end

#display_columns(config = {}) ⇒ Object Also known as: display_new

Display prompt_menu in columns using commandwindow This is an improved way of showing the “most” like menu. The earlier format would only print in one row.



381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
# File 'lib/rbcurse/core/include/io.rb', line 381

def display_columns config={}
  prompt = config[:prompt] || "Choose: "
  require 'rbcurse/core/util/rcommandwindow'
  layout = { :height => 5, :width => Ncurses.COLS-0, :top => Ncurses.LINES-6, :left => 0 }
  rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title] || "Menu"
  w = rc.window
  r = 4
  c = 1
  color = $datacolor
  begin
    menu = @options
    $log.debug " DISP MENU "
    ret = 0
    len = 80
    while true
      h = {}
      valid = []
      labels = []
      menu.each{ |item|
        if item.respond_to? :hotkey
          hk = item.hotkey.to_s
        else
          raise ArgumentError, "Promptmenu needs hotkey or mnemonic"
        end
        # 187compat 2013-03-20 - 19:00 throws up
        labels << "%c. %s " % [ hk.getbyte(0), item.label ]
        h[hk] = item
        valid << hk
      }
      #$log.debug " valid are #{valid} "
      color = $datacolor
      #print_this(win, str, color, r, c)
      rc.display_menu labels, :indexing => :custom
      ch=w.getchar()
      rc.clear
      #$log.debug " got ch #{ch} "
      next if ch < 0 or ch > 255
      if ch == 3 || ch == ?\C-g.getbyte(0)
        clear_this w, r, c, color, len
        print_this(w, "Aborted.", color, r,c)
        break
      end
      ch = ch.chr
      index = valid.index ch
      if index.nil?
        clear_this w, r, c, color, len
        print_this(w, "Not valid. Valid are #{valid}. C-c/C-g to abort.", color, r,c)
        sleep 1
        next
      end
      #$log.debug " index is #{index} "
      item = h[ch]
      # I don;t think this even shows now, its useless
      if item.respond_to? :desc
        desc = item.desc
        #desc ||= "Could not find desc for #{ch} "
        desc ||= ""
        clear_this w, r, c, color, len
        print_this(w, desc, color, r,c)
      end
      action = item.action
      case action
        #when Array
      when PromptMenu
        # submenu
        menu = action.options
        title = rc.title
        rc.title title +" => " + action.text # set title of window to submenu
      when Proc
        ret = action.call
        break
      when Symbol
        if @caller.respond_to?(action, true)
          $log.debug "XXX:  IO caller responds to action #{action} "
          ret = @caller.send(action)
        elsif @caller.respond_to?(:execute_this, true)
          ret = @caller.send(:execute_this, action)
        else
          alert "PromptMenu: unidentified action #{action} for #{@caller.class} "
          raise "PromptMenu: unidentified action #{action} for #{@caller.class} "
        end

        break
      else 
        $log.debug " Unidentified flying class #{action.class} "
        break
      end
    end # while
  ensure
    rc.destroy
    rc = nil
  end
end

create the whole thing using a MenuTree which has minimal information. It uses a hotkey and a code only. We are supposed to resolve the display text and actual proc from the caller using this code.



357
358
359
360
361
362
363
364
365
366
367
368
# File 'lib/rbcurse/core/include/io.rb', line 357

def menu_tree mt, pm = self
  mt.each_pair { |ch, code| 
    if code.is_a? RubyCurses::MenuTree
      item = pm.add(ch, code.value, "") 
      current = PromptMenu.new @caller, code.value
      item.action = current
      menu_tree code, current
    else
      item = pm.add(ch, code.to_s, "", code) 
    end
  }
end

To allow a more rubyesque way of defining menus and submenus



371
372
373
374
375
# File 'lib/rbcurse/core/include/io.rb', line 371

def submenu key, label, &block
  item = CMenuItem.new(key, label)
  @options << item
  item.action = PromptMenu.new @caller, label, &block
end