#============================================================================== # ■ Window_Select [Ver.2.5.1] by Claimh #------------------------------------------------------------------------------ #  カーソルの移動やスクロールの機能を持つウィンドウクラスです。 #------------------------------------------------------------------------------ # Window_Selectableのグレードアップ版 # ・項目数が多いとハングアップする問題修正(文字列キャッシュ導入を推奨) # ・無駄にWindow.updateしてた部分の削除 # ・毎フレームupdate_helpが呼ばれる仕様を変更(indexが変わったときのみ) # ・スーパークラスとしての機能不足を改善 # ・行の高さを指定できるように拡張 # ・Windowクラスでopacity/contents_opacityを0にしても上下左右カーソルが消えない問題の回避 # ・最終選択indexを記憶する仕組みを追加 #------------------------------------------------------------------------------ # [制限] # ・Window_Selectableと同様に左右のページ切り替えには対応してない # ・@column_max > 2 はnot support扱い(先頭行での↑遷移が少し変) #------------------------------------------------------------------------------ # [使い方メモ] # ・@commandsにメニューのリストを入れれば基本OK(@commandsに基づいて描画) # ・index更新は@indexではなくself.indexを使う # ・以下のメソッドは必要に応じて継承したClassで再定義する # draw_item : 項目描画 # index_active? : 項目の選択有効判定(super必須) # update_help : help_windowの更新(indexが変わった時しか呼ばない) # ・SceneClassは current_index_active? で選択可否を判定する # ・self.updown_cursolで上下のカーソルの表示ON/OFF # ・@ajust_cursor=trueは@commands[i]のテキストを元にAjustする(text以外は使用不可) #============================================================================== class Window_Select < Window_Base #-------------------------------------------------------------------------- # ● 公開インスタンス変数 #-------------------------------------------------------------------------- attr_reader :index # カーソル位置 attr_reader :help_window # ヘルプウィンドウ #-------------------------------------------------------------------------- # ● オブジェクト初期化 # x : ウィンドウの X 座標 # y : ウィンドウの Y 座標 # width : ウィンドウの幅 # height : ウィンドウの高さ # t_size : 行の高さ(デフォルト:32) #-------------------------------------------------------------------------- def initialize(x, y, width, height, t_size=32) super(x, y, width, height) self.windowskin = RPG::Cache.windowskin($game_system.windowskin_name) self.contents = Bitmap.new(width - 32, height - 32) self.contents.init_font self.visible = true @item_max = 1 # リスト数 = @commands.size @column_max = 1 # 列数 @row_h = t_size # 行の高さ @base_width = width # window幅 @base_height = height # window高さ @index = 0 # index @last_index = 0 # ラストindex @base_index = 0 # 表示される先頭index @updown_cursol = true # ↑↓カーソル表示ON @i_refresh = false # index変化時に毎回refresh @enable_L_R = true # L / Rボタン @ajust_cursor = false # カーソル矩形を文字にフィットさせる @pos_up_plus = 0 @pos_down_plus = 0 @commands = [] # アイテムリスト end #-------------------------------------------------------------------------- # ● データサイズ #-------------------------------------------------------------------------- def data_size return @item_max end #-------------------------------------------------------------------------- # ● リフレッシュ #-------------------------------------------------------------------------- def refresh update_updown_cursol # ↑↓カーソルの表示判定(&bitmap.clear) return if @item_max == 0 or @commands.empty? max_index = @base_index + page_row_max * @column_max max_index = @item_max if max_index > @item_max for i in @base_index...max_index # 実際に表示される部分だけ描画 draw_item(i) end #p "refresh", self end #-------------------------------------------------------------------------- # ● 項目の描画 # index : 項目番号 #-------------------------------------------------------------------------- def draw_item(i) set_index_color(i) self.contents.draw_shadow_cache_text( index_pos_x(i)+4, index_pos_y(i), text_w, @row_h, @commands[i] ) end #-------------------------------------------------------------------------- # ● 項目を消す #-------------------------------------------------------------------------- def erase_item(i) rect = Rect.new(index_pos_x(i), index_pos_y(i), text_w, @row_h) self.contents.fill_rect(rect, Color.new(0, 0, 0, 0)) end #-------------------------------------------------------------------------- # ● 項目を無効化し、再描画 #-------------------------------------------------------------------------- def disable_item(i) erase_item(i) # 無効色で再描画 self.contents.font.color = disabled_color self.contents.draw_shadow_cache_text( index_pos_x(i)+4, index_pos_y(i), text_w, @row_h, @commands[i] ) end #-------------------------------------------------------------------------- # ● 項目:X位置の取得 #-------------------------------------------------------------------------- def index_pos_x(index) return (index - @base_index) % @column_max * (self.contents.width / @column_max) end def current_index_pos_x return index_pos_x(@index) end #-------------------------------------------------------------------------- # ● 項目:Y位置の取得 #-------------------------------------------------------------------------- def index_pos_y(index) return ((index - @base_index) / @column_max * @row_h + @pos_up_plus) end def current_index_pos_y return index_pos_y(@index) end #-------------------------------------------------------------------------- # ● 項目:描画範囲 #-------------------------------------------------------------------------- def text_w return self.contents.width / @column_max end #-------------------------------------------------------------------------- # ● 項目の色設定 #-------------------------------------------------------------------------- def set_index_color(i) # 選択肢が有効か? if index_active?(i) self.contents.font.color = normal_color return 255 # opacity用 else self.contents.font.color = disabled_color return 127 # opacity用 end end #-------------------------------------------------------------------------- # ● カーソル有効判定(SuperClassはnilチェックのみ) #-------------------------------------------------------------------------- def index_active?(index) return false if index < 0 return false if @commands[index].nil? return true end def current_index_active? return index_active?(@index) end #-------------------------------------------------------------------------- # ● カーソル情報取得 #-------------------------------------------------------------------------- def item(i) return nil if i < 0 return @commands[i] end def current_item return item(@index) end #-------------------------------------------------------------------------- # ● 画面上に見えているINDEX #-------------------------------------------------------------------------- def view_index return @index - @base_index end #-------------------------------------------------------------------------- # ● カーソル位置の設定 # index : 新しいカーソル位置 #-------------------------------------------------------------------------- def index=(index) index_refresh(index) end #-------------------------------------------------------------------------- # ● カーソル位置の設定 # index : 新しいカーソル位置 # force : true=強制refresh / false=変化あればrefresh #-------------------------------------------------------------------------- def index_refresh(index, force=false) # indexに変化がなければ何もしない return if @index == index and !force @index = index # ヘルプテキストを更新 (update_help は継承先で定義される) if self.active and @help_window != nil update_help end org_base_index = @base_index # カーソルの矩形を更新 update_cursor_rect # 内容の書き換え refresh if force or @i_refresh or org_base_index != @base_index end #-------------------------------------------------------------------------- # ● ラストindexを記憶 #-------------------------------------------------------------------------- def keep @last_index = @index self.active = false self.index = -1 end #-------------------------------------------------------------------------- # ● ラストindexを復元 #-------------------------------------------------------------------------- def remember self.active = true index_refresh(@last_index, true) end #-------------------------------------------------------------------------- # ● 指定indexでremember #-------------------------------------------------------------------------- def remember_i(index) @last_index = @item_max > index ? index : (@item_max - 1) remember end #-------------------------------------------------------------------------- # ● ラストindexを忘れる #-------------------------------------------------------------------------- def forget @last_index = 0 end #-------------------------------------------------------------------------- # ● Window有効化 #-------------------------------------------------------------------------- def enable self.active = true draw_cursor end def disable self.active = false undraw_cursor end #-------------------------------------------------------------------------- # ● ↑↓カーソルの表示 #-------------------------------------------------------------------------- def update_updown_cursol org_pos_up_plus = @pos_up_plus org_pos_down_plus = @pos_down_plus @pos_up_plus = 0 @pos_down_plus = 0 if @updown_cursol # ↑↓カーソル表示有効 @pos_up_plus = 10 if up_cursol? @pos_down_plus = 10 if down_cursol? end if org_pos_up_plus == @pos_up_plus and org_pos_down_plus == @pos_down_plus self.contents.clear return # カーソル表示変化がなければbitmapをクリアして終了 end height = @base_height + @pos_up_plus + @pos_down_plus self.contents.dispose # 古いのは破棄し、新しく作り直す self.contents = Bitmap.new(@base_width - 32, height - 32) self.contents.init_font # フォント初期化 self.oy = @pos_up_plus end #-------------------------------------------------------------------------- # ● ↑↓カーソルが必要か? #-------------------------------------------------------------------------- def up_cursol? return (@base_index / @column_max + page_row_max) > page_row_max end def down_cursol? return (@base_index / @column_max + page_row_max ) < row_max end #-------------------------------------------------------------------------- # ● ↑↓カーソルの表示ON/OFF #-------------------------------------------------------------------------- def updown_cursol=(flag) return if @updown_corsol == flag @updown_cursol = flag if up_cursol? or down_cursol? refresh # カーソル表示が必要だった場合、refresh end end #-------------------------------------------------------------------------- # ● 行数の取得 #-------------------------------------------------------------------------- def row_max # 項目数と列数から行数を算出 return 1 if @item_max == 0 return (@item_max + @column_max - 1) / @column_max end #-------------------------------------------------------------------------- # ● 先頭の行の取得 #-------------------------------------------------------------------------- def top_row # ウィンドウ内容の転送元 Y 座標を、1 行の高さ 32 で割る return @base_index end #-------------------------------------------------------------------------- # ● 先頭の行の設定 # row : 先頭に表示する行 #-------------------------------------------------------------------------- def top_row=(row) # row が row_max - 1 超の場合は row_max - 1 に修正 if row > row_max - 1 row = row_max - 1 end # row が 0 未満の場合は 0 に修正 if row < 0 row = 0 end @base_index = row * @column_max end #-------------------------------------------------------------------------- # ● 1 ページに表示できる行数の取得 #-------------------------------------------------------------------------- def page_row_max # ウィンドウの高さを1 行の高さ @row_h で割る return ((@base_height - 32) / @row_h).truncate end #-------------------------------------------------------------------------- # ● 1 ページに表示できる項目数の取得 #-------------------------------------------------------------------------- def page_item_max # 行数 page_row_max に 列数 @column_max を掛ける return page_row_max * @column_max end #-------------------------------------------------------------------------- # ● ヘルプウィンドウの設定 # help_window : 新しいヘルプウィンドウ #-------------------------------------------------------------------------- def help_window=(help_window) @help_window = help_window # ヘルプテキストを更新 (update_help は継承先で定義される) if self.active and @help_window != nil update_help end end #-------------------------------------------------------------------------- # ● ヘルプテキスト更新 #-------------------------------------------------------------------------- def update_help # スーパークラスでは空メソッドにしておく end #-------------------------------------------------------------------------- # ● カーソルの矩形更新 #-------------------------------------------------------------------------- def update_cursor_rect # 現在の行が、表示されている先頭の行より前の場合 if @index < @base_index # 現在の行が先頭になるようにスクロール self.top_row = (@index / @column_max).truncate end # 現在の行が、表示されている最後尾の行より後ろの場合 if @index >= (@base_index + self.page_item_max) # 現在の行が最後尾になるようにスクロール self.top_row = (@index / @column_max).truncate - self.page_row_max + 1 end # カーソル位置が 0 未満の場合はカーソル表示しない @index < 0 ? undraw_cursor : draw_cursor end #-------------------------------------------------------------------------- # ● カーソル表示 #-------------------------------------------------------------------------- def draw_cursor # カーソルの幅を計算 cursor_width = @ajust_cursor ? (self.contents.text_size(@commands[@index]).width + 8) : (self.contents.width / @column_max) # カーソルの矩形を更新 self.cursor_rect.set(index_pos_x(@index), index_pos_y(@index)-@pos_up_plus, cursor_width, @row_h) end #-------------------------------------------------------------------------- # ● カーソル消去 #-------------------------------------------------------------------------- def undraw_cursor self.cursor_rect.empty end #-------------------------------------------------------------------------- # ● フレーム更新 #-------------------------------------------------------------------------- def update # 無駄なWindow.updateを制限する return if !self.visible # カーソル系の移動はアクティブ時のみ return if !self.active or @index < 0 super # Item数が0以下なら処理しない return if @item_max <= 0 # 方向ボタンの下が押された場合 if Input.repeat?(Input::DOWN) and @item_max > @column_max # 最上位の行にいる場合は最終項目に移動 if (@index < (@item_max - @column_max)) or Input.trigger?(Input::DOWN) $game_system.se_play($data_system.cursor_se) # カーソルを下に移動 if (@index + @column_max) < @item_max self.index = @index + @column_max else self.index = (@index + @column_max) % @column_max end end end # 方向ボタンの上が押された場合 if Input.repeat?(Input::UP) and @item_max > @column_max # 列数が 1 かつ 方向ボタンの上の押下状態がリピートでない場合か、 # またはカーソル位置が列数より後ろの場合 if (@index >= @column_max) or Input.trigger?(Input::UP) $game_system.se_play($data_system.cursor_se) # カーソルを上に移動 if (@index - @column_max) >= 0 self.index = @index - @column_max else case @column_max when 1 self.index = @item_max - 1 when 2 self.index = (@item_max % @column_max) == 0 ? ((@index - @column_max + @item_max) % @item_max) : (@item_max - 1) when 3 # @column_max > 2 is not support self.index = (@index - @column_max + @item_max) % @item_max end end end end # 方向ボタンの右が押された場合 if Input.repeat?(Input::RIGHT) # 列数が 2 以上で、カーソル位置が(項目数 - 1)より前の場合 if @column_max >= 2 and @index < @item_max - 1 $game_system.se_play($data_system.cursor_se) # カーソルを右に移動 self.index += 1 elsif Input.trigger?(Input::RIGHT) and @column_max > 1 $game_system.se_play($data_system.cursor_se) self.index = 0 end end # 方向ボタンの左が押された場合 if Input.repeat?(Input::LEFT) # 列数が 2 以上で、カーソル位置が 0 より後ろの場合 if @column_max >= 2 and @index > 0 $game_system.se_play($data_system.cursor_se) # カーソルを左に移動 self.index -= 1 elsif Input.trigger?(Input::LEFT) and @column_max > 1 # カーソルを上に移動 $game_system.se_play($data_system.cursor_se) self.index = @item_max - 1 end end # L / R が無効なら処理しない return unless @enable_L_R # R ボタンが押された場合で最終Indexでなければ if Input.repeat?(Input::R) and @index < (@item_max - 1) $game_system.se_play($data_system.cursor_se) # カーソルを 1 ページ後ろに移動 self.index = [@index + self.page_item_max, @item_max - 1].min end # L ボタンが押された場合で先頭Indexでなければ if Input.repeat?(Input::L) and @index > 0 $game_system.se_play($data_system.cursor_se) # カーソルを 1 ページ前に移動 self.index = [@index - self.page_item_max, 0].max end end end