//-----------------------------------------------------------------------------
// Window_Selectable
//
// The window class with cursor movement functions.
/**
* The window class with cursor movement functions.
*
* @class
* @extends Window_Scrollable
*/
function Window_Selectable() {
this.initialize(...arguments);
}
Window_Selectable.prototype = Object.create(Window_Scrollable.prototype);
Window_Selectable.prototype.constructor = Window_Selectable;
Window_Selectable.prototype.initialize = function(rect) {
Window_Scrollable.prototype.initialize.call(this, rect);
this._index = -1;
this._cursorFixed = false;
this._cursorAll = false;
this._helpWindow = null;
this._handlers = {};
this._doubleTouch = false;
this._canRepeat = true;
this.deactivate();
};
/**
* Get the current index
*
* @return {number} The currently selected index
*/
Window_Selectable.prototype.index = function() {
return this._index;
};
/**
* Gheck if the cursor is fixed
*
* @return {boolean} True if the cursor is fixed
*/
Window_Selectable.prototype.cursorFixed = function() {
return this._cursorFixed;
};
/**
* Set the cursor fixed flag
*
* @param {boolean} cursorFixed - The new cursor fixed setting
*/
Window_Selectable.prototype.setCursorFixed = function(cursorFixed) {
this._cursorFixed = cursorFixed;
};
/**
* Get the cursor all flag
*
* @return {boolean} The cursor all flag
*/
Window_Selectable.prototype.cursorAll = function() {
return this._cursorAll;
};
/**
* Set the cursor all flag
*
* @param {boolean} cursorAll - The new cursor all setting
*/
Window_Selectable.prototype.setCursorAll = function(cursorAll) {
this._cursorAll = cursorAll;
};
/**
* Get the amount of columns
*
* @return {number} Amount of columns
*/
Window_Selectable.prototype.maxCols = function() {
return 1;
};
/**
* Get the amount of items
*
* @return {number} Amount of items
*/
Window_Selectable.prototype.maxItems = function() {
return 0;
};
/**
* Get the spacing between columns
*
* @return {number} Spacing between columns
*/
Window_Selectable.prototype.colSpacing = function() {
return 8;
};
/**
* Get the spacing between rows
*
* @return {number} Spacing between rows
*/
Window_Selectable.prototype.rowSpacing = function() {
return 4;
};
/**
* Get the width of an item
*
* @return {number} Width of one item
*/
Window_Selectable.prototype.itemWidth = function() {
return Math.floor(this.innerWidth / this.maxCols());
};
Window_Selectable.prototype.itemHeight = function() {
return Window_Scrollable.prototype.itemHeight.call(this) + 8;
};
/**
* Get the height of the contents
*
* @return {number} Contents height
*/
Window_Selectable.prototype.contentsHeight = function() {
return this.innerHeight + this.itemHeight();
};
/**
* Get number of rows
*
* @return {number} Amount of rows
*/
Window_Selectable.prototype.maxRows = function() {
return Math.max(Math.ceil(this.maxItems() / this.maxCols()), 1);
};
/**
* Get the total height
*
* @return {number} Total height
*/
Window_Selectable.prototype.overallHeight = function() {
return this.maxRows() * this.itemHeight();
};
Window_Selectable.prototype.activate = function() {
Window_Scrollable.prototype.activate.call(this);
this.reselect();
};
Window_Selectable.prototype.deactivate = function() {
Window_Scrollable.prototype.deactivate.call(this);
this.reselect();
};
/**
* Selects the given index
*
* @param {number} index - The index to select
*/
Window_Selectable.prototype.select = function(index) {
this._index = index;
this.refreshCursor();
this.callUpdateHelp();
};
/**
* Forcibly select an index
*
* @param {number} index - The index to select
*/
Window_Selectable.prototype.forceSelect = function(index) {
this.select(index);
this.ensureCursorVisible(false);
};
/**
* Smoothly selects an index
*
* @param {number} index - The index to select
*/
Window_Selectable.prototype.smoothSelect = function(index) {
this.select(index);
this.ensureCursorVisible(true);
};
/**
* Deselects from the window
*/
Window_Selectable.prototype.deselect = function() {
this.select(-1);
};
/**
* Reselects the last index that was selected
*/
Window_Selectable.prototype.reselect = function() {
this.select(this._index);
this.ensureCursorVisible(true);
this.cursorVisible = true;
};
/**
* Gets the current row
*
* @return {number} The current row
*/
Window_Selectable.prototype.row = function() {
return Math.floor(this.index() / this.maxCols());
};
/**
* Gets the top row
*
* @return {number} The top row
*/
Window_Selectable.prototype.topRow = function() {
return Math.floor(this.scrollY() / this.itemHeight());
};
/**
* Gets the max top row
*
* @return {number} The max top row
*/
Window_Selectable.prototype.maxTopRow = function() {
return Math.max(0, this.maxRows() - this.maxPageRows());
};
/**
* Sets the top row by scrolling to the row
*
* @param {number} row - The row to scroll to
*/
Window_Selectable.prototype.setTopRow = function(row) {
this.scrollTo(this.scrollX(), row * this.itemHeight());
};
/**
* Gets the number of rows in a page
*
* @return {number} The number of rows in a page
*/
Window_Selectable.prototype.maxPageRows = function() {
return Math.floor(this.innerHeight / this.itemHeight());
};
/**
* Gets the number of items in a page
*
* @return {number} The number of items in a page
*/
Window_Selectable.prototype.maxPageItems = function() {
return this.maxPageRows() * this.maxCols();
};
/**
* Gets the number of visible items
*
* @return {number} The number of visible items
*/
Window_Selectable.prototype.maxVisibleItems = function() {
const visibleRows = Math.ceil(this.contentsHeight() / this.itemHeight());
return visibleRows * this.maxCols();
};
/**
* Check if there is only one row
*
* @return {boolean} True if only one row
*/
Window_Selectable.prototype.isHorizontal = function() {
return this.maxPageRows() === 1;
};
/**
* Gets the top index
*
* @return {number} The top index
*/
Window_Selectable.prototype.topIndex = function() {
return this.topRow() * this.maxCols();
};
/**
* Gets the rectangle that covers the item at the given index
*
* @param {number} index - The index of the item
* @return {Rectangle} The rectangle that covers the item
*/
Window_Selectable.prototype.itemRect = function(index) {
const maxCols = this.maxCols();
const itemWidth = this.itemWidth();
const itemHeight = this.itemHeight();
const colSpacing = this.colSpacing();
const rowSpacing = this.rowSpacing();
const col = index % maxCols;
const row = Math.floor(index / maxCols);
const x = col * itemWidth + colSpacing / 2 - this.scrollBaseX();
const y = row * itemHeight + rowSpacing / 2 - this.scrollBaseY();
const width = itemWidth - colSpacing;
const height = itemHeight - rowSpacing;
return new Rectangle(x, y, width, height);
};
/**
* Gets the rectangle that covers the item at the given index, after applying x padding
*
* @param {number} index - The index of the item
* @return {Rectangle} The rectangle that covers the item
*/
Window_Selectable.prototype.itemRectWithPadding = function(index) {
const rect = this.itemRect(index);
const padding = this.itemPadding();
rect.x += padding;
rect.width -= padding * 2;
return rect;
};
/**
* Gets the rectangle that covers the item at the given index, after applying x and y padding
*
* @param {number} index - The index of the item
* @return {Rectangle} The rectangle that covers the item
*/
Window_Selectable.prototype.itemLineRect = function(index) {
const rect = this.itemRectWithPadding(index);
const padding = (rect.height - this.lineHeight()) / 2;
rect.y += padding;
rect.height -= padding * 2;
return rect;
};
/**
* Associates the given help window with this window
*
* @param {Window_Help} helpWindow - The help window
*/
Window_Selectable.prototype.setHelpWindow = function(helpWindow) {
this._helpWindow = helpWindow;
this.callUpdateHelp();
};
/**
* Shows the associated help window
*/
Window_Selectable.prototype.showHelpWindow = function() {
if (this._helpWindow) {
this._helpWindow.show();
}
};
/**
* Hides the associated help window
*/
Window_Selectable.prototype.hideHelpWindow = function() {
if (this._helpWindow) {
this._helpWindow.hide();
}
};
/**
* Sets a handler function
*
* @param {string} symbol - The id (or symbol) of the function
* @param {Function} method - The method to call when the symbol is called
*/
Window_Selectable.prototype.setHandler = function(symbol, method) {
this._handlers[symbol] = method;
};
/**
* Check if there is a handler function for a symbol
*
* @param {string} symbol - The id (or symbol) of the function
* @return {boolean} True if a function exists for that symbol
*/
Window_Selectable.prototype.isHandled = function(symbol) {
return !!this._handlers[symbol];
};
/**
* Calls a handler function
*
* @param {string} symbol - The id (or symbol) of the function
*/
Window_Selectable.prototype.callHandler = function(symbol) {
if (this.isHandled(symbol)) {
this._handlers[symbol]();
}
};
/**
* Check if the window is open and active
*
* @return {boolean} True if the window is both open and active (and visible)
*/
Window_Selectable.prototype.isOpenAndActive = function() {
return this.isOpen() && this.visible && this.active;
};
/**
* Check if the cursor can be moved
*
* @return {boolean} True if the cursor can be moved
*/
Window_Selectable.prototype.isCursorMovable = function() {
return (
this.isOpenAndActive() &&
!this._cursorFixed &&
!this._cursorAll &&
this.maxItems() > 0
);
};
/**
* Handling for when the cursor moves down
*
* @param {boolean} wrap - If the cursor should wrap
*/
Window_Selectable.prototype.cursorDown = function(wrap) {
const index = this.index();
const maxItems = this.maxItems();
const maxCols = this.maxCols();
if (index < maxItems - maxCols || (wrap && maxCols === 1)) {
this.smoothSelect((index + maxCols) % maxItems);
}
};
/**
* Handling for when the cursor moves up
*
* @param {boolean} wrap - If the cursor should wrap
*/
Window_Selectable.prototype.cursorUp = function(wrap) {
const index = Math.max(0, this.index());
const maxItems = this.maxItems();
const maxCols = this.maxCols();
if (index >= maxCols || (wrap && maxCols === 1)) {
this.smoothSelect((index - maxCols + maxItems) % maxItems);
}
};
/**
* Handling for when the cursor moves right
*
* @param {boolean} wrap - If the cursor should wrap
*/
Window_Selectable.prototype.cursorRight = function(wrap) {
const index = this.index();
const maxItems = this.maxItems();
const maxCols = this.maxCols();
const horizontal = this.isHorizontal();
if (maxCols >= 2 && (index < maxItems - 1 || (wrap && horizontal))) {
this.smoothSelect((index + 1) % maxItems);
}
};
/**
* Handling for when the cursor moves left
*
* @param {boolean} wrap - If the cursor should wrap
*/
Window_Selectable.prototype.cursorLeft = function(wrap) {
const index = Math.max(0, this.index());
const maxItems = this.maxItems();
const maxCols = this.maxCols();
const horizontal = this.isHorizontal();
if (maxCols >= 2 && (index > 0 || (wrap && horizontal))) {
this.smoothSelect((index - 1 + maxItems) % maxItems);
}
};
/**
* Handling for when the cursor moves by pagedown
*/
Window_Selectable.prototype.cursorPagedown = function() {
const index = this.index();
const maxItems = this.maxItems();
if (this.topRow() + this.maxPageRows() < this.maxRows()) {
this.smoothScrollDown(this.maxPageRows());
this.select(Math.min(index + this.maxPageItems(), maxItems - 1));
}
};
/**
* Handling for when the cursor moves by pageup
*/
Window_Selectable.prototype.cursorPageup = function() {
const index = this.index();
if (this.topRow() > 0) {
this.smoothScrollUp(this.maxPageRows());
this.select(Math.max(index - this.maxPageItems(), 0));
}
};
/**
* Check if scrolling is enabled
*
* @return {boolean} True if scrolling is enabled
*/
Window_Selectable.prototype.isScrollEnabled = function() {
return this.active || this.index() < 0;
};
Window_Selectable.prototype.update = function() {
this.processCursorMove();
this.processHandling();
this.processTouch();
Window_Scrollable.prototype.update.call(this);
};
/**
* Processing for a cursor move
*/
Window_Selectable.prototype.processCursorMove = function() {
if (this.isCursorMovable()) {
const lastIndex = this.index();
if (Input.isRepeated("down")) {
this.cursorDown(Input.isTriggered("down"));
}
if (Input.isRepeated("up")) {
this.cursorUp(Input.isTriggered("up"));
}
if (Input.isRepeated("right")) {
this.cursorRight(Input.isTriggered("right"));
}
if (Input.isRepeated("left")) {
this.cursorLeft(Input.isTriggered("left"));
}
if (!this.isHandled("pagedown") && Input.isTriggered("pagedown")) {
this.cursorPagedown();
}
if (!this.isHandled("pageup") && Input.isTriggered("pageup")) {
this.cursorPageup();
}
if (this.index() !== lastIndex) {
this.playCursorSound();
}
}
};
/**
* Processing for handled methods
*/
Window_Selectable.prototype.processHandling = function() {
if (this.isOpenAndActive()) {
if (this.isOkEnabled() && this.isOkTriggered()) {
return this.processOk();
}
if (this.isCancelEnabled() && this.isCancelTriggered()) {
return this.processCancel();
}
if (this.isHandled("pagedown") && Input.isTriggered("pagedown")) {
return this.processPagedown();
}
if (this.isHandled("pageup") && Input.isTriggered("pageup")) {
return this.processPageup();
}
}
};
/**
* Processing for when touched
*/
Window_Selectable.prototype.processTouch = function() {
if (this.isOpenAndActive()) {
if (this.isHoverEnabled() && TouchInput.isHovered()) {
this.onTouchSelect(false);
} else if (TouchInput.isTriggered()) {
this.onTouchSelect(true);
}
if (TouchInput.isClicked()) {
this.onTouchOk();
} else if (TouchInput.isCancelled()) {
this.onTouchCancel();
}
}
};
/**
* Determine if hovering is enabled
*
* @return {boolean} True if hovering is enabled
*/
Window_Selectable.prototype.isHoverEnabled = function() {
return true;
};
/**
* Handling for when touch is selected
*
* @param {boolean} trigger - If triggered or just hovered
*/
Window_Selectable.prototype.onTouchSelect = function(trigger) {
this._doubleTouch = false;
if (this.isCursorMovable()) {
const lastIndex = this.index();
const hitIndex = this.hitIndex();
if (hitIndex >= 0) {
if (hitIndex === this.index()) {
this._doubleTouch = true;
}
this.select(hitIndex);
}
if (trigger && this.index() !== lastIndex) {
this.playCursorSound();
}
}
};
/**
* Handling for touch OK input
*/
Window_Selectable.prototype.onTouchOk = function() {
if (this.isTouchOkEnabled()) {
const hitIndex = this.hitIndex();
if (this._cursorFixed) {
if (hitIndex === this.index()) {
this.processOk();
}
} else if (hitIndex >= 0) {
this.processOk();
}
}
};
/**
* Handling for touch cancel input
*/
Window_Selectable.prototype.onTouchCancel = function() {
if (this.isCancelEnabled()) {
this.processCancel();
}
};
/**
* Check which index is hit
*
* @return {number} The index that is hit
*/
Window_Selectable.prototype.hitIndex = function() {
const touchPos = new Point(TouchInput.x, TouchInput.y);
const localPos = this.worldTransform.applyInverse(touchPos);
return this.hitTest(localPos.x, localPos.y);
};
/**
* Test if an index is hit at given x/y coordinates
*
* @param {number} x - The x coordinate to check
* @param {number} y - The y coordinate to check
* @return {number} The index at the given coordinates
*/
Window_Selectable.prototype.hitTest = function(x, y) {
if (this.innerRect.contains(x, y)) {
const cx = this.origin.x + x - this.padding;
const cy = this.origin.y + y - this.padding;
const topIndex = this.topIndex();
for (let i = 0; i < this.maxVisibleItems(); i++) {
const index = topIndex + i;
if (index < this.maxItems()) {
const rect = this.itemRect(index);
if (rect.contains(cx, cy)) {
return index;
}
}
}
}
return -1;
};
/**
* Check if touch OK is enabled
*
* @return {boolean} True if touch OK is enabled
*/
Window_Selectable.prototype.isTouchOkEnabled = function() {
return (
this.isOkEnabled() &&
(this._cursorFixed || this._cursorAll || this._doubleTouch)
);
};
/**
* Check if OK is enabled
*
* @return {boolean} True if OK is enabled
*/
Window_Selectable.prototype.isOkEnabled = function() {
return this.isHandled("ok");
};
/**
* Check if cancel is enabled
*
* @return {boolean} True if cancel is enabled
*/
Window_Selectable.prototype.isCancelEnabled = function() {
return this.isHandled("cancel");
};
/**
* Check if OK input is triggered
*
* @return {boolean} True if OK input is triggered
*/
Window_Selectable.prototype.isOkTriggered = function() {
return this._canRepeat ? Input.isRepeated("ok") : Input.isTriggered("ok");
};
/**
* Check if cancel input is triggered
*
* @return {boolean} True if cancel input is triggered
*/
Window_Selectable.prototype.isCancelTriggered = function() {
return Input.isRepeated("cancel");
};
/**
* Processing for ok input
*/
Window_Selectable.prototype.processOk = function() {
if (this.isCurrentItemEnabled()) {
this.playOkSound();
this.updateInputData();
this.deactivate();
this.callOkHandler();
} else {
this.playBuzzerSound();
}
};
/**
* Calls the ok handler function
*/
Window_Selectable.prototype.callOkHandler = function() {
this.callHandler("ok");
};
/**
* Processing for cancel input
*/
Window_Selectable.prototype.processCancel = function() {
SoundManager.playCancel();
this.updateInputData();
this.deactivate();
this.callCancelHandler();
};
/**
* Calls the cancel handler function
*/
Window_Selectable.prototype.callCancelHandler = function() {
this.callHandler("cancel");
};
/**
* Processing for pageup
*/
Window_Selectable.prototype.processPageup = function() {
this.updateInputData();
this.deactivate();
this.callHandler("pageup");
};
/**
* Processing for pagedown
*/
Window_Selectable.prototype.processPagedown = function() {
this.updateInputData();
this.deactivate();
this.callHandler("pagedown");
};
/**
* Update input data
*/
Window_Selectable.prototype.updateInputData = function() {
Input.update();
TouchInput.update();
this.clearScrollStatus();
};
/**
* Ensure the cursor is visible by scrolling if needed
*
* @param {boolean} smooth - Whether to smooth scroll if cursor is not visible
*/
Window_Selectable.prototype.ensureCursorVisible = function(smooth) {
if (this._cursorAll) {
this.scrollTo(0, 0);
} else if (this.innerHeight > 0 && this.row() >= 0) {
const scrollY = this.scrollY();
const itemTop = this.row() * this.itemHeight();
const itemBottom = itemTop + this.itemHeight();
const scrollMin = itemBottom - this.innerHeight;
if (scrollY > itemTop) {
if (smooth) {
this.smoothScrollTo(0, itemTop);
} else {
this.scrollTo(0, itemTop);
}
} else if (scrollY < scrollMin) {
if (smooth) {
this.smoothScrollTo(0, scrollMin);
} else {
this.scrollTo(0, scrollMin);
}
}
}
};
/**
* Calls the updateHelp function if the window is active and there is a help window
*/
Window_Selectable.prototype.callUpdateHelp = function() {
if (this.active && this._helpWindow) {
this.updateHelp();
}
};
/**
* Updates the help window
*/
Window_Selectable.prototype.updateHelp = function() {
this._helpWindow.clear();
};
/**
* Sets the help window item
*
* @param {*} item - The object to pass to the help window
*/
Window_Selectable.prototype.setHelpWindowItem = function(item) {
if (this._helpWindow) {
this._helpWindow.setItem(item);
}
};
/**
* Check if the item at the currently selected index is enabled
*
* @return {boolean} True if the current item is enabled
*/
Window_Selectable.prototype.isCurrentItemEnabled = function() {
return true;
};
/**
* Draws all the items
*/
Window_Selectable.prototype.drawAllItems = function() {
const topIndex = this.topIndex();
for (let i = 0; i < this.maxVisibleItems(); i++) {
const index = topIndex + i;
if (index < this.maxItems()) {
this.drawItemBackground(index);
this.drawItem(index);
}
}
};
/**
* Draws an individual item
*/
Window_Selectable.prototype.drawItem = function(/*index*/) {
//
};
/**
* Clears the item at the given index
*
* @param {number} index - The index of the item to clear
*/
Window_Selectable.prototype.clearItem = function(index) {
const rect = this.itemRect(index);
this.contents.clearRect(rect.x, rect.y, rect.width, rect.height);
this.contentsBack.clearRect(rect.x, rect.y, rect.width, rect.height);
};
/**
* Draws the background at the given index
*
* @param {number} index - The index to draw the background
*/
Window_Selectable.prototype.drawItemBackground = function(index) {
const rect = this.itemRect(index);
this.drawBackgroundRect(rect);
};
/**
* Draws a background rect
*
* @param {Rectangle} rect - The rectangle for where to draw the background
*/
Window_Selectable.prototype.drawBackgroundRect = function(rect) {
const c1 = ColorManager.itemBackColor1();
const c2 = ColorManager.itemBackColor2();
const x = rect.x;
const y = rect.y;
const w = rect.width;
const h = rect.height;
this.contentsBack.gradientFillRect(x, y, w, h, c1, c2, true);
this.contentsBack.strokeRect(x, y, w, h, c1);
};
/**
* Redraws the item at the given index
*
* @param {number} index - The index of the item to redraw
*/
Window_Selectable.prototype.redrawItem = function(index) {
if (index >= 0) {
this.clearItem(index);
this.drawItemBackground(index);
this.drawItem(index);
}
};
/**
* Redraws the currently selected item
*/
Window_Selectable.prototype.redrawCurrentItem = function() {
this.redrawItem(this.index());
};
/**
* Refreshes the window
*/
Window_Selectable.prototype.refresh = function() {
this.paint();
};
/**
* Clears and then draws the window content
*/
Window_Selectable.prototype.paint = function() {
if (this.contents) {
this.contents.clear();
this.contentsBack.clear();
this.drawAllItems();
}
};
/**
* Refreshes the cursor
*/
Window_Selectable.prototype.refreshCursor = function() {
if (this._cursorAll) {
this.refreshCursorForAll();
} else if (this.index() >= 0) {
const rect = this.itemRect(this.index());
this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
} else {
this.setCursorRect(0, 0, 0, 0);
}
};
/**
* Refreshes the cursor for all
*/
Window_Selectable.prototype.refreshCursorForAll = function() {
const maxItems = this.maxItems();
if (maxItems > 0) {
const rect = this.itemRect(0);
rect.enlarge(this.itemRect(maxItems - 1));
this.setCursorRect(rect.x, rect.y, rect.width, rect.height);
} else {
this.setCursorRect(0, 0, 0, 0);
}
};