twterm 2.8.0 → 2.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,18 +1,17 @@
1
1
  module Twterm
2
2
  class ColorManager
3
3
  include Singleton
4
- include Curses
5
4
 
6
5
  COLORS = [:black, :white, :red, :green, :blue, :yellow, :cyan, :magenta, :transparent]
7
6
  CURSES_COLORS = {
8
- black: COLOR_BLACK,
9
- white: COLOR_WHITE,
10
- red: COLOR_RED,
11
- green: COLOR_GREEN,
12
- blue: COLOR_BLUE,
13
- yellow: COLOR_YELLOW,
14
- cyan: COLOR_CYAN,
15
- magenta: COLOR_MAGENTA,
7
+ black: Curses::COLOR_BLACK,
8
+ white: Curses::COLOR_WHITE,
9
+ red: Curses::COLOR_RED,
10
+ green: Curses::COLOR_GREEN,
11
+ blue: Curses::COLOR_BLUE,
12
+ yellow: Curses::COLOR_YELLOW,
13
+ cyan: Curses::COLOR_CYAN,
14
+ magenta: Curses::COLOR_MAGENTA,
16
15
  transparent: -1
17
16
  }
18
17
 
data/lib/twterm/image.rb CHANGED
@@ -7,6 +7,7 @@ require 'twterm/image/empty'
7
7
  require 'twterm/image/horizontal_sequential_image'
8
8
  require 'twterm/image/parens'
9
9
  require 'twterm/image/string_image'
10
+ require 'twterm/image/underlined'
10
11
  require 'twterm/image/vertical_sequential_image'
11
12
 
12
13
  class Twterm::Image
@@ -14,6 +15,10 @@ class Twterm::Image
14
15
  @column, @line = column, line
15
16
  end
16
17
 
18
+ def _
19
+ underlined
20
+ end
21
+
17
22
  def !
18
23
  bold
19
24
  end
@@ -101,6 +106,27 @@ class Twterm::Image
101
106
  StringImage.new(str)
102
107
  end
103
108
 
109
+ # @param items [Array<Symbol>]
110
+ # @param selected [Symbol]
111
+ #
112
+ # @return [Image]
113
+ def self.toggle_switch(items, selected)
114
+ items
115
+ .map do |item|
116
+ on = item == selected
117
+ string(item.to_s)
118
+ .bold(on)
119
+ .underlined(on)
120
+ end
121
+ .intersperse(string(' | '))
122
+ .reduce(empty) { |acc, x| acc - x }
123
+ .brackets
124
+ end
125
+
126
+ def underlined(on = true)
127
+ on ? Underlined.new(self) : self
128
+ end
129
+
104
130
  def self.whitespace
105
131
  string(' ')
106
132
  end
@@ -0,0 +1,31 @@
1
+ module Twterm
2
+ class Image
3
+ class Underlined < Twterm::Image
4
+ def initialize(image)
5
+ @image = image
6
+ end
7
+
8
+ def height
9
+ image.height
10
+ end
11
+
12
+ def render(window)
13
+ window.attron(Curses::A_UNDERLINE)
14
+ image.at(line, column).render(window)
15
+ window.attroff(Curses::A_UNDERLINE)
16
+ end
17
+
18
+ def to_s
19
+ "\e[4m#{image}\e[0m"
20
+ end
21
+
22
+ def width
23
+ image.width
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :image
29
+ end
30
+ end
31
+ end
@@ -1,6 +1,7 @@
1
1
  require 'twterm/image'
2
2
 
3
3
  module Twterm
4
+ # @todo Rename to `Presenter`
4
5
  module ImageBuilder
5
6
  class UserNameImageBuilder
6
7
  COLORS = [:red, :blue, :green, :cyan, :yellow, :magenta].freeze
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
  require 'singleton'
3
3
 
4
4
  require 'twterm/app'
@@ -105,14 +105,12 @@ module Twterm
105
105
  end
106
106
 
107
107
  def load_dict_file!
108
- dict = TOML.load_file(dict_file_path, symbolize_keys: true)
109
- rescue TOML::ParseError, TOML::ValueOverwriteError => e
108
+ dict = TomlRB.load_file(dict_file_path, symbolize_keys: true)
109
+ rescue TomlRB::ParseError => e
110
110
  first_line =
111
111
  case e
112
- when TOML::ParseError
113
- "Your key assignments dictionary file (#{dict_file_path}) could not be parsed"
114
- when TOML::ValueOverwriteError
115
- "Command `#{e.key}` is declared more than once"
112
+ when TomlRB::ParseError
113
+ "Your key assignments dictionary file (#{dict_file_path}) could not be parsed:\n#{e.message}"
116
114
  end
117
115
 
118
116
  warn <<-EOS
@@ -132,7 +130,7 @@ Press any key to continue
132
130
  end
133
131
 
134
132
  def save!(mappings)
135
- dict = TOML.dump(mappings).gsub("\n[", "\n\n[")
133
+ dict = TomlRB.dump(mappings).gsub("\n[", "\n\n[")
136
134
  File.open(dict_file_path, 'w', 0644) { |f| f.write(dict) }
137
135
  end
138
136
  end
data/lib/twterm/list.rb CHANGED
@@ -1,7 +1,41 @@
1
1
  module Twterm
2
+ # A Twitter list
2
3
  class List
3
- attr_reader :id, :name, :slug, :full_name, :mode, :description, :member_count, :subscriber_count, :url
4
+ # Unique ID of the list
5
+ #
6
+ # @return [Integer]
7
+ attr_reader :id
4
8
 
9
+ # @return [String]
10
+ attr_reader :name
11
+
12
+ # @return [String]
13
+ attr_reader :slug
14
+
15
+ # @return [String]
16
+ attr_reader :full_name
17
+
18
+ attr_reader :mode
19
+
20
+ # @return [String]
21
+ attr_reader :description
22
+
23
+ # The number of users that are in this list
24
+ #
25
+ # @return [Integer]
26
+ attr_reader :member_count
27
+
28
+ # The number of users that subscribe this list
29
+ #
30
+ # @return [Integer]
31
+ attr_reader :subscriber_count
32
+
33
+ # @return [String]
34
+ attr_reader :url
35
+
36
+ # @param other [List]
37
+ #
38
+ # @return [Boolean]
5
39
  def ==(other)
6
40
  other.is_a?(self.class) && id == other.id
7
41
  end
@@ -11,6 +45,7 @@ module Twterm
11
45
  update!(list)
12
46
  end
13
47
 
48
+ # @return [self]
14
49
  def update!(list)
15
50
  @name = list.name
16
51
  @slug = list.slug
@@ -5,11 +5,10 @@ require 'twterm/event/screen/resize'
5
5
  module Twterm
6
6
  class MessageWindow
7
7
  include Singleton
8
- include Curses
9
8
  include Subscriber
10
9
 
11
10
  def initialize
12
- @window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 2, 0)
11
+ @window = Curses.stdscr.subwin(1, Curses.stdscr.maxx, Curses.stdscr.maxy - 1, 0)
13
12
  @queue = Queue.new
14
13
 
15
14
  subscribe(Event::Message::AbstractMessage) do |e|
@@ -34,7 +33,7 @@ module Twterm
34
33
 
35
34
  def show(message = nil)
36
35
  loop do
37
- break unless closed?
36
+ break unless Curses.closed?
38
37
  sleep 0.5
39
38
  end
40
39
 
@@ -74,8 +73,8 @@ module Twterm
74
73
  end
75
74
 
76
75
  def resize(_event)
77
- @window.resize(1, stdscr.maxx)
78
- @window.move(stdscr.maxy - 2, 0)
76
+ @window.resize(1, Curses.stdscr.maxx)
77
+ @window.move(Curses.stdscr.maxy - 1, 0)
79
78
  end
80
79
  end
81
80
  end
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
 
3
3
  module Twterm
4
4
  class PersistableConfigurationProxy
@@ -32,16 +32,16 @@ module Twterm
32
32
  # @param [String] filepath File path to load configuration from
33
33
  # @return [Twterm::PersistableConfigurationProxy] a configuration proxy
34
34
  def self.load_from_file!(klass, filepath)
35
- config = TOML.load_file(filepath, symbolize_keys: true)
35
+ config = TomlRB.load_file(filepath, symbolize_keys: true)
36
36
  new(klass.new(config), filepath).migrate!
37
37
  rescue Errno::ENOENT
38
38
  new(klass.default, filepath)
39
- rescue TOML::ParseError, TOML::ValueOverwriteError => e
39
+ rescue TomlRB::ParseError, TomlRB::ValueOverwriteError => e
40
40
  msg =
41
41
  case e
42
- when TOML::ParseError
42
+ when TomlRB::ParseError
43
43
  "Your configuration file could not be parsed"
44
- when TOML::ValueOverwriteError
44
+ when TomlRB::ValueOverwriteError
45
45
  "`#{e.key}` is declared more than once"
46
46
  end
47
47
 
@@ -75,7 +75,7 @@ Press any key to continue
75
75
  attr_reader :filepath, :instance
76
76
 
77
77
  def persist!
78
- hash = TOML.dump(instance.to_h).gsub("\n[", "\n\n[")
78
+ hash = TomlRB.dump(instance.to_h).gsub("\n[", "\n\n[")
79
79
  File.open(filepath, 'w', 0644) { |f| f.write(hash) }
80
80
  end
81
81
  end
@@ -1,4 +1,4 @@
1
- require 'toml'
1
+ require 'toml-rb'
2
2
 
3
3
  require 'twterm/abstract_persistable_configuration'
4
4
 
@@ -27,6 +27,9 @@ module Twterm
27
27
  # @return [Twterm::Preferences] an instance having the default value
28
28
  def self.default
29
29
  new({
30
+ control: {
31
+ scroll_direction: 'traditional',
32
+ },
30
33
  photo_viewer_backend: {
31
34
  browser: true,
32
35
  imgcat: false,
@@ -48,8 +51,12 @@ module Twterm
48
51
  # @return [Hash]
49
52
  def self.structure
50
53
  bool = -> x { x == true || x == false }
54
+ scroll_direction = -> x { x == 'natural' || x == 'traditional' }
51
55
 
52
56
  {
57
+ control: {
58
+ scroll_direction: scroll_direction
59
+ },
53
60
  photo_viewer_backend: {
54
61
  browser: bool,
55
62
  imgcat: bool,
data/lib/twterm/screen.rb CHANGED
@@ -6,18 +6,18 @@ require 'twterm/subscriber'
6
6
  module Twterm
7
7
  class Screen
8
8
  include Subscriber
9
- include Curses
10
9
 
11
10
  def initialize(app, client)
12
11
  @app, @client = app, client
13
12
 
14
- @screen = init_screen
15
- noecho
16
- raw
17
- curs_set(0)
18
- stdscr.keypad(true)
19
- start_color
20
- use_default_colors
13
+ @screen = Curses.init_screen
14
+ Curses.noecho
15
+ Curses.raw
16
+ Curses.curs_set(0)
17
+ Curses.stdscr.keypad(true)
18
+ Curses.start_color
19
+ Curses.use_default_colors
20
+ Curses.mousemask(Curses::BUTTON1_CLICKED | 65536 | 2097152)
21
21
 
22
22
  subscribe(Event::Screen::Refresh) { refresh }
23
23
  subscribe(Event::Screen::Resize, :resize)
@@ -53,6 +53,42 @@ module Twterm
53
53
 
54
54
  attr_reader :app, :client
55
55
 
56
+ # @param [Integer, String] key
57
+ def handle_keyboard_event(key)
58
+ return if app.tab_manager.current_tab.respond_to_key(key)
59
+ return if app.tab_manager.respond_to_key(key)
60
+ respond_to_key(key)
61
+ end
62
+
63
+ # @param [Curses::MouseEvent] e
64
+ def handle_mouse_event(e)
65
+ x = e.x
66
+ y = e.y
67
+
68
+ case e.bstate
69
+ when Curses::BUTTON1_CLICKED
70
+ return app.tab_manager.handle_left_click(x, y) if app.tab_manager.enclose?(x, y)
71
+ when 65536
72
+ scroll_direction = app.preferences[:control, :scroll_direction]
73
+
74
+ case scroll_direction
75
+ when 'natural'
76
+ return app.tab_manager.handle_scroll_up(x, y) if app.tab_manager.enclose?(x, y)
77
+ when 'traditional'
78
+ return app.tab_manager.handle_scroll_down(x, y) if app.tab_manager.enclose?(x, y)
79
+ end
80
+ when 2097152
81
+ scroll_direction = app.preferences[:control, :scroll_direction]
82
+
83
+ case scroll_direction
84
+ when 'natural'
85
+ return app.tab_manager.handle_scroll_down(x, y) if app.tab_manager.enclose?(x, y)
86
+ when 'traditional'
87
+ return app.tab_manager.handle_scroll_up(x, y) if app.tab_manager.enclose?(x, y)
88
+ end
89
+ end
90
+ end
91
+
56
92
  def refresh
57
93
  app.tab_manager.refresh_window
58
94
  app.tab_manager.current_tab.render
@@ -60,10 +96,10 @@ module Twterm
60
96
  end
61
97
 
62
98
  def resize(event)
63
- return if closed?
99
+ return if Curses.closed?
64
100
 
65
101
  lines, cols = event.lines, event.cols
66
- resizeterm(lines, cols)
102
+ Curses.resizeterm(lines, cols)
67
103
  @screen.resize(lines, cols)
68
104
 
69
105
  refresh
@@ -72,11 +108,15 @@ module Twterm
72
108
  def scan
73
109
  app.reset_interruption_handler
74
110
 
75
- key = getch
111
+ key = Curses.getch
76
112
 
77
- return if app.tab_manager.current_tab.respond_to_key(key)
78
- return if app.tab_manager.respond_to_key(key)
79
- respond_to_key(key)
113
+ if key == Curses::Key::MOUSE
114
+ e = Curses.getmouse
115
+
116
+ handle_mouse_event(e) unless e.nil?
117
+ else
118
+ handle_keyboard_event(key)
119
+ end
80
120
  end
81
121
  end
82
122
  end
@@ -3,7 +3,6 @@ require 'twterm/subscriber'
3
3
 
4
4
  module Twterm
5
5
  class SearchQueryWindow
6
- include Curses
7
6
  include Singleton
8
7
  include Subscriber
9
8
 
@@ -12,7 +11,7 @@ module Twterm
12
11
  attr_reader :last_query
13
12
 
14
13
  def initialize
15
- @window = stdscr.subwin(1, stdscr.maxx, stdscr.maxy - 1, 0)
14
+ @window = Curses.stdscr.subwin(1, Curses.stdscr.maxx, Curses.stdscr.maxy - 1, 0)
16
15
  @searching_down = true
17
16
  @str = ''
18
17
  @last_query = ''
@@ -29,7 +28,7 @@ module Twterm
29
28
  chars = []
30
29
 
31
30
  loop do
32
- char = getch
31
+ char = Curses.getch
33
32
 
34
33
  if char.nil?
35
34
  case chars.first
@@ -117,8 +116,8 @@ module Twterm
117
116
  attr_reader :window
118
117
 
119
118
  def resize(_event)
120
- window.resize(1, stdscr.maxx)
121
- window.move(stdscr.maxy - 1, 0)
119
+ window.resize(1, Curses.stdscr.maxx)
120
+ window.move(Curses.stdscr.maxy - 1, 0)
122
121
  end
123
122
 
124
123
  def render(str)
data/lib/twterm/status.rb CHANGED
@@ -12,6 +12,7 @@ class Twitter::Tweet
12
12
  end
13
13
 
14
14
  module Twterm
15
+ # A tweet
15
16
  class Status
16
17
  attr_reader :created_at, :favorite_count, :favorited, :hashtags, :id,
17
18
  :in_reply_to_status_id, :media, :retweet_count, :retweeted,
@@ -23,11 +24,13 @@ module Twterm
23
24
  other.is_a?(self.class) && id == other.id
24
25
  end
25
26
 
27
+ # @todo This should be done in a presenter
26
28
  def date
27
29
  format = Time.now - @created_at < 86_400 ? '%H:%M:%S' : '%Y-%m-%d %H:%M:%S'
28
30
  @created_at.strftime(format)
29
31
  end
30
32
 
33
+ # @todo This can be marked as private
31
34
  def expand_url!
32
35
  sub = -> (x) { @text.sub!(x.url, x.display_url) }
33
36
  (@media + @urls).each(&sub)
@@ -69,10 +72,16 @@ module Twterm
69
72
  expand_url!
70
73
  end
71
74
 
75
+ # Is this status a quote?
76
+ #
77
+ # @return [Boolean]
72
78
  def quote?
73
79
  !quoted_status_id.nil?
74
80
  end
75
81
 
82
+ # Is this status a retweet?
83
+ #
84
+ # @return [Boolean]
76
85
  def retweet?
77
86
  !retweeted_status_id.nil?
78
87
  end
@@ -93,6 +102,7 @@ module Twterm
93
102
  @retweeted = false
94
103
  end
95
104
 
105
+ # @return [self]
96
106
  def update!(tweet, is_retweeted_status = false)
97
107
  @retweet_count = tweet.retweet_count
98
108
  @favorite_count = tweet.favorite_count