carousel/list view

Signed-off-by: crueter <swurl@swurl.xyz>
This commit is contained in:
crueter 2025-06-17 00:29:05 -04:00 committed by crueter
parent 0e13a362f1
commit 97d648cac6
Signed by: crueter
GPG key ID: 425ACD2D4830EBC6
9 changed files with 244 additions and 65 deletions

View file

@ -282,13 +282,13 @@ void QtConfig::ReadUIGamelistValues() {
ReadCategory(Settings::Category::UiGameList);
const int favorites_size = BeginArray("favorites");
for (int i = 0; i < favorites_size; i++) {
SetArrayIndex(i);
UISettings::values.favorited_ids.append(
ReadUnsignedIntegerSetting(std::string("program_id")));
}
EndArray();
// const int favorites_size = BeginArray("favorites");
// for (int i = 0; i < favorites_size; i++) {
// SetArrayIndex(i);
// UISettings::values.favorited_ids.append(
// ReadUnsignedIntegerSetting(std::string("program_id")));
// }
// EndArray();
EndGroup();
}
@ -490,12 +490,12 @@ void QtConfig::SaveUIGamelistValues()
WriteCategory(Settings::Category::UiGameList);
BeginArray(std::string("favorites"));
for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
SetArrayIndex(i);
WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
}
EndArray(); // favorites
// BeginArray(std::string("favorites"));
// for (int i = 0; i < UISettings::values.favorited_ids.size(); i++) {
// SetArrayIndex(i);
// WriteIntegerSetting(std::string("program_id"), UISettings::values.favorited_ids[i]);
// }
// EndArray(); // favorites
EndGroup();
}

View file

@ -59,6 +59,12 @@ enum class Theme {
MidnightBlueColorful,
};
enum class GameView {
Grid,
List,
Carousel,
};
static constexpr Theme default_theme{
#ifdef _WIN32
Theme::DarkColorful
@ -192,15 +198,15 @@ struct Values {
std::pair<std::vector<std::string>, std::vector<std::string>> multiplayer_ban_list;
// Game List
Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
std::atomic_bool is_game_list_reload_pending{false};
Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
QVector<u64> favorited_ids;
// Setting<bool> show_add_ons{linkage, true, "show_add_ons", Category::UiGameList};
// Setting<u32> game_icon_size{linkage, 64, "game_icon_size", Category::UiGameList};
// Setting<u32> folder_icon_size{linkage, 48, "folder_icon_size", Category::UiGameList};
// Setting<u8> row_1_text_id{linkage, 3, "row_1_text_id", Category::UiGameList};
// Setting<u8> row_2_text_id{linkage, 2, "row_2_text_id", Category::UiGameList};
// std::atomic_bool is_game_list_reload_pending{false};
// Setting<bool> cache_game_list{linkage, true, "cache_game_list", Category::UiGameList};
// Setting<bool> favorites_expanded{linkage, true, "favorites_expanded", Category::UiGameList};
// QVector<u64> favorited_ids;
Setting<u8, true> grid_columns{linkage, 4, 1, 8, "grid_columns", Category::UiGameList};

View file

@ -56,9 +56,6 @@ void GameListModel::reload()
qreal size = entry.size();
QString sizeString = QLocale::system().formattedDataSize(size);
qDebug() << path << name << size;
// m_data << Game{path, name, size};
QStandardItem *game = new QStandardItem(name);
game->setData(path, GLMRoleTypes::PATH);
game->setData(sizeString, GLMRoleTypes::FILESIZE);

View file

@ -9,5 +9,10 @@ qt_add_qml_module(edenMain
Main.qml
StatusBar.qml
GameList.qml
GamePreview.qml
GameGridCard.qml
MarqueeText.qml
GameGrid.qml
GameCarouselCard.qml
GameCarousel.qml
)

View file

@ -0,0 +1,94 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
import org.eden_emu.interface
ListView {
id: carousel
focus: true
focusPolicy: Qt.StrongFocus
model: EdenGameList
orientation: ListView.Horizontal
clip: false
flickDeceleration: 1000
snapMode: ListView.SnapToItem
onHeightChanged: console.log(width, height)
spacing: 20
Keys.enabled: true
Keys.onRightPressed: incrementCurrentIndex()
Keys.onLeftPressed: decrementCurrentIndex()
onCurrentIndexChanged: scrollToCenter()
highlight: Rectangle {
id: hg
clip: false
z: 3
color: "transparent"
border {
color: "deepskyblue"
width: 4 * Constants.scalar
}
radius: 8 * Constants.scalar
// TODO: marquee
Text {
function toTitleCase(str) {
return str.replace(/\w\S*/g, text => text.charAt(0).toUpperCase(
) + text.substring(1).toLowerCase())
}
property var item: carousel.currentItem
text: toTitleCase(item.title)
font.pixelSize: 22 * Constants.scalar
color: "lightblue"
anchors {
bottom: hg.top
bottomMargin: 10 * Constants.scalar
left: hg.left
right: hg.right
}
horizontalAlignment: Text.AlignHCenter
elide: Text.ElideRight
}
}
highlightFollowsCurrentItem: true
highlightMoveDuration: 300
highlightMoveVelocity: -1
delegate: GameCarouselCard {
id: game
width: 300
height: 300
}
function scrollToCenter() {
let targetX = currentIndex * 320 - (width - 320) / 2
let min = 0
let max = contentWidth
contentX = Math.max(min, Math.min(max, targetX))
}
Behavior on contentX {
NumberAnimation {
duration: 300
easing.type: Easing.OutQuad
}
}
}

View file

@ -0,0 +1,44 @@
import QtQuick
import QtQuick.Controls
import Qt.labs.platform
import QtCore
import org.eden_emu.constants
import org.eden_emu.interface
import org.eden_emu.gamepad
GridView {
property var setting
id: grid
property int cellSize: Math.floor(width / setting.value)
highlightFollowsCurrentItem: true
clip: true
cellWidth: cellSize
cellHeight: cellSize + 60 * Constants.scalar
model: EdenGameList
delegate: GameGridCard {
id: game
width: grid.cellSize - 20 * Constants.scalar
height: grid.cellHeight - 20 * Constants.scalar
}
highlight: Rectangle {
color: "transparent"
z: 5
radius: 16 * Constants.scalar
border {
color: Constants.text
width: 3
}
}
focus: true
focusPolicy: "StrongFocus"
}

View file

@ -20,15 +20,16 @@ Rectangle {
color: Constants.bg
// TODO: make this optional.
// Probably just make a Gamepad frontend/backend split with a null backend
// TODO: use the original yuzu backend for dis
Gamepad {
id: gamepad
onUpPressed: grid.moveCurrentIndexUp()
onDownPressed: grid.moveCurrentIndexDown()
onLeftPressed: grid.moveCurrentIndexLeft()
onRightPressed: grid.moveCurrentIndexRight()
// onUpPressed: grid.moveCurrentIndexUp()
// onDownPressed: grid.moveCurrentIndexDown()
// onLeftPressed: grid.moveCurrentIndexLeft()
// onRightPressed: grid.moveCurrentIndexRight()
onLeftPressed: carousel.decrementCurrentIndex()
onRightPressed: carousel.incrementCurrentIndex()
onAPressed: console.log("A pressed")
onLeftStickMoved: (x, y) => {
gx = x
@ -54,67 +55,56 @@ Rectangle {
}
}
}
Timer {
interval: 16
running: true
repeat: true
onTriggered: gamepad.pollEvents()
}
FolderDialog {
id: openDir
folder: StandardPaths.writableLocation(StandardPaths.HomeLocation)
onAccepted: {
button.visible = false
grid.anchors.bottom = root.bottom
view.anchors.bottom = root.bottom
EdenGameList.addDir(folder)
}
}
GridView {
id: grid
// GameGrid {
// setting: parent.setting
property int cellSize: Math.floor(width / setting.value)
// id: grid
highlightFollowsCurrentItem: true
clip: true
cellWidth: cellSize
cellHeight: cellSize + 60 * Constants.scalar
// anchors.bottom: button.top
// anchors.left: parent.left
// anchors.margins: 8
// anchors.right: parent.right
// anchors.top: parent.top
// }
Item {
id: view
anchors {
top: parent.top
bottom: button.top
left: parent.left
right: parent.right
bottom: button.top
margins: 8
top: parent.top
margins: 8 * Constants.scalar
}
model: EdenGameList
GameCarousel {
id: carousel
delegate: GamePreview {
id: game
height: 300
width: grid.cellSize - 20 * Constants.scalar
height: grid.cellHeight - 20 * Constants.scalar
}
anchors {
right: view.right
left: view.left
highlight: Rectangle {
color: "transparent"
z: 5
radius: 16 * Constants.scalar
border {
color: Constants.text
width: 3
verticalCenter: view.verticalCenter
}
}
focus: true
focusPolicy: "StrongFocus"
}
Button {

View file

@ -0,0 +1,43 @@
import QtQuick
Item {
required property string text
property int spacing: 30
property int startDelay: 2000
property int speed: 40
property alias font: t1.font
property alias color: t1.color
id: root
width: t1.width + spacing
height: t1.height
clip: true
Text {
id: t1
SequentialAnimation on x {
loops: Animation.Infinite
running: true
PauseAnimation {
duration: root.startDelay
}
NumberAnimation {
from: root.width
to: -t1.width
duration: (root.width + t1.width) * 1000 / root.speed
easing.type: Easing.Linear
}
}
Text {
x: root.width
text: t1.text
}
}
}