Class: Interactive

Inherits:
Object
  • Object
show all
Defined in:
lib/interactive.rb

Constant Summary collapse

KEY_TOP =
"\e[A"
KEY_RIGHT =
"\e[C"
KEY_DOWN =
"\e[B"
KEY_LEFT =
"\e[D"
KEY_BACKSPACE =
"\x7F"
KEY_ENTER =
"\r"
KEY_CTRL_C =
"\x03"
KEY_TAB =
"\t"
KEY_ESCAPE =
"\e"
SEQ_ERASE_LEFT =
"\e[D"
SEQ_ERASE_TO_END_OF_LINE =
"\033[K"

Instance Method Summary collapse

Constructor Details

#initializeInteractive

Returns a new instance of Interactive.



15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/interactive.rb', line 15

def initialize
  @term_width = `/usr/bin/env tput cols`.to_i
  @formatter = TaskFormatter.new

  if @term_width < @formatter.max_width
    message = " Your terminal must be at least #{@formatter.max_width} columns wide "
    STDERR.puts('<' + message.
                  rjust(@formatter.max_width + 1 - message.length / 2, '-').
                  ljust(@formatter.max_width - 2, '-') + '>')
    exit(1)
  end
end

Instance Method Details

#interactive_edit(tasks) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/interactive.rb', line 78

def interactive_edit(tasks)
  @tasks = tasks
  @deleted_tasks = []
  @header = '2015-06-03  ' + '[↑|↓] navigate, [⇽|⇾] adjust time, [⇐ ] remove, [↵ |q] save, [esc] cancel'.grey

  @task_selected = 0

  loop do
    print_tasks
    input = read_char

    exit if [KEY_CTRL_C, KEY_ESCAPE].include? input
    return @deleted_tasks if [KEY_ENTER, 'q'].include? input

    top_down = { KEY_DOWN => +1, KEY_TOP => -1 }
    if top_down.keys.include? input
      @task_selected += top_down[input]
      @task_selected %= @tasks.length
    end
    left_right = { KEY_RIGHT => +300, KEY_LEFT => -300 }
    if left_right.keys.include? input
      time = @tasks[@task_selected].duration
      time = [time - (time % 300) + left_right[input], 0].max
      @tasks[@task_selected].duration = time
      @tasks[@task_selected].edited_at = Time.now
    end
    if input == KEY_BACKSPACE
      next if @tasks.length <= 1
      @tasks[@task_selected].deleted_at = Time.now
      @deleted_tasks << @tasks[@task_selected]
      @tasks.delete_at(@task_selected)
      @task_selected -= 1 if @task_selected == @tasks.length
      @task_selected %= @tasks.length
    end
  end

end


56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/interactive.rb', line 56

def print_tasks
  task_len = TaskFormatter.max_length(@tasks)

  puts @header
  @tasks.each_with_index do |task, i|
    @formatter.format(task, task_len)

    print "\e[47m" + "\e[30m" if i == @task_selected
    print @formatter.line_for_interactive
    print "\e[0m" if i == @task_selected
    print '  ' + @formatter.sync_status
    print "\033[K"
    puts ''
  end
  total_time = Utils.format_time(@tasks.map(&:duration).reduce(:+))
  puts "\e[100m  #{''.ljust(task_len, ' ')} #{total_time}  #{''.rjust(@formatter.bar_size, ' ')}  \e[0m\033[K"

  puts "\033[K"
  print "\033[#{@tasks.length + 3}A"
end

#read_charObject



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/interactive.rb', line 28

def read_char
  begin
    # save previous state of stty
    old_state = `stty -g`
    # disable echoing and enable raw (not having to press enter)
    system "stty raw -echo"
    c = STDIN.getc.chr
    # gather next two characters of special keys
    if (c=="\e")
      extra_thread = Thread.new {
        c = c + STDIN.getc.chr
        c = c + STDIN.getc.chr
      }
      # wait just long enough for special keys to get swallowed
      extra_thread.join(0.00001)
      # kill thread so not-so-long special keys don't wait on getc
      extra_thread.kill
    end
  rescue => ex
    puts "#{ex.class}: #{ex.message}"
    puts ex.backtrace
  ensure
    # restore previous state of stty
    system "stty #{old_state}"
  end
  return c
end