Skip to content

fix: Page Navigation State #933

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Open
wants to merge 9 commits into
base: Alpha-v9.5.3
Choose a base branch
from
56 changes: 27 additions & 29 deletions src/tagstudio/core/library/alchemy/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import structlog

from tagstudio.core.query_lang.ast import AST, Constraint, ConstraintType
from tagstudio.core.query_lang.ast import AST
from tagstudio.core.query_lang.parser import Parser

MAX_SQL_VARIABLES = 32766 # 32766 is the max sql bind parameter count as defined here: https://github.com/sqlite/sqlite/blob/master/src/sqliteLimit.h#L140
Expand Down Expand Up @@ -72,59 +72,57 @@ class SortingModeEnum(enum.Enum):


@dataclass
class FilterState:
class BrowsingState:
"""Represent a state of the Library grid view."""

# these should remain
page_size: int
page_index: int = 0
sorting_mode: SortingModeEnum = SortingModeEnum.DATE_ADDED
ascending: bool = True

# these should be erased on update
# Abstract Syntax Tree Of the current Search Query
ast: AST | None = None

@property
def limit(self):
return self.page_size
query: str | None = None

# Abstract Syntax Tree Of the current Search Query
@property
def offset(self):
return self.page_size * self.page_index
def ast(self) -> AST | None:
if self.query is None:
return None
return Parser(self.query).parse()

@classmethod
def show_all(cls, page_size: int) -> "FilterState":
return FilterState(page_size=page_size)
def show_all(cls) -> "BrowsingState":
return BrowsingState()

@classmethod
def from_search_query(cls, search_query: str, page_size: int) -> "FilterState":
return cls(ast=Parser(search_query).parse(), page_size=page_size)
def from_search_query(cls, search_query: str) -> "BrowsingState":
return cls(query=search_query)

@classmethod
def from_tag_id(cls, tag_id: int | str, page_size: int) -> "FilterState":
return cls(ast=Constraint(ConstraintType.TagID, str(tag_id), []), page_size=page_size)
def from_tag_id(cls, tag_id: int | str) -> "BrowsingState":
return cls(query=f"tag_id:{str(tag_id)}")

@classmethod
def from_path(cls, path: Path | str, page_size: int) -> "FilterState":
return cls(ast=Constraint(ConstraintType.Path, str(path).strip(), []), page_size=page_size)
def from_path(cls, path: Path | str) -> "BrowsingState":
return cls(query=f'path:"{str(path).strip()}"')

@classmethod
def from_mediatype(cls, mediatype: str, page_size: int) -> "FilterState":
return cls(ast=Constraint(ConstraintType.MediaType, mediatype, []), page_size=page_size)
def from_mediatype(cls, mediatype: str) -> "BrowsingState":
return cls(query=f"mediatype:{mediatype}")

@classmethod
def from_filetype(cls, filetype: str, page_size: int) -> "FilterState":
return cls(ast=Constraint(ConstraintType.FileType, filetype, []), page_size=page_size)
def from_filetype(cls, filetype: str) -> "BrowsingState":
return cls(query=f"filetype:{filetype}")

@classmethod
def from_tag_name(cls, tag_name: str, page_size: int) -> "FilterState":
return cls(ast=Constraint(ConstraintType.Tag, tag_name, []), page_size=page_size)
def from_tag_name(cls, tag_name: str) -> "BrowsingState":
return cls(query=f'tag:"{tag_name}"')

def with_page_index(self, index: int) -> "BrowsingState":
return replace(self, page_index=index)

def with_sorting_mode(self, mode: SortingModeEnum) -> "FilterState":
def with_sorting_mode(self, mode: SortingModeEnum) -> "BrowsingState":
return replace(self, sorting_mode=mode)

def with_sorting_direction(self, ascending: bool) -> "FilterState":
def with_sorting_direction(self, ascending: bool) -> "BrowsingState":
return replace(self, ascending=ascending)


Expand Down
9 changes: 5 additions & 4 deletions src/tagstudio/core/library/alchemy/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
from tagstudio.core.library.alchemy.db import make_tables
from tagstudio.core.library.alchemy.enums import (
MAX_SQL_VARIABLES,
BrowsingState,
FieldTypeEnum,
FilterState,
SortingModeEnum,
)
from tagstudio.core.library.alchemy.fields import (
Expand Down Expand Up @@ -857,13 +857,14 @@ def get_paths(self, glob: str | None = None, limit: int = -1) -> list[str]:

def search_library(
self,
search: FilterState,
search: BrowsingState,
page_size: int,
) -> SearchResult:
"""Filter library by search query.

:return: number of entries matching the query and one page of results.
"""
assert isinstance(search, FilterState)
assert isinstance(search, BrowsingState)
assert self.engine

with Session(self.engine, expire_on_commit=False) as session:
Expand Down Expand Up @@ -902,7 +903,7 @@ def search_library(
sort_on = func.lower(Entry.path)

statement = statement.order_by(asc(sort_on) if search.ascending else desc(sort_on))
statement = statement.limit(search.limit).offset(search.offset)
statement = statement.limit(page_size).offset(search.page_index * page_size)

logger.info(
"searching library",
Expand Down
4 changes: 2 additions & 2 deletions src/tagstudio/core/utils/dupe_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import structlog

from tagstudio.core.library.alchemy.enums import FilterState
from tagstudio.core.library.alchemy.enums import BrowsingState
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Entry

Expand Down Expand Up @@ -52,7 +52,7 @@ def refresh_dupe_files(self, results_filepath: str | Path):
continue

results = self.library.search_library(
FilterState.from_path(path_relative, page_size=500),
BrowsingState.from_path(path_relative), 500
)

if not results:
Expand Down
4 changes: 2 additions & 2 deletions src/tagstudio/qt/modals/fix_unlinked.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def __init__(self, library: "Library", driver: "QtDriver"):
self.relink_class.done.connect(
# refresh the grid
lambda: (
self.driver.filter_items(),
self.driver.update_browsing_state(),
self.refresh_missing_files(),
)
)
Expand All @@ -78,7 +78,7 @@ def __init__(self, library: "Library", driver: "QtDriver"):
lambda: (
self.set_missing_count(),
# refresh the grid
self.driver.filter_items(),
self.driver.update_browsing_state(),
)
)
self.delete_button.clicked.connect(self.delete_modal.show)
Expand Down
6 changes: 2 additions & 4 deletions src/tagstudio/qt/modals/tag_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
)

from tagstudio.core.constants import RESERVED_TAG_END, RESERVED_TAG_START
from tagstudio.core.library.alchemy.enums import FilterState, TagColorEnum
from tagstudio.core.library.alchemy.enums import BrowsingState, TagColorEnum
from tagstudio.core.library.alchemy.library import Library
from tagstudio.core.library.alchemy.models import Tag
from tagstudio.core.palette import ColorType, get_tag_color
Expand Down Expand Up @@ -292,9 +292,7 @@ def set_tag_widget(self, tag: Tag | None, index: int):
tag_widget.search_for_tag_action.triggered.connect(
lambda checked=False, tag_id=tag.id: (
self.driver.main_window.searchField.setText(f"tag_id:{tag_id}"),
self.driver.filter_items(
FilterState.from_tag_id(tag_id, page_size=self.driver.settings.page_size)
),
self.driver.update_browsing_state(BrowsingState.from_tag_id(tag_id)),
)
)
tag_widget.search_for_tag_action.setEnabled(True)
Expand Down
Loading