Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions configurator/configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ def load_ui(builder: Gtk.Builder) -> None:
background: alpha(@theme_fg_color, 0.12);
font-size: 0.85em;
}
.option-value {
font-size: 1.35em;
font-weight: 700;
}
.selected-option {
background: alpha(@theme_selected_bg_color, 0.10);
}
Expand Down Expand Up @@ -330,8 +334,16 @@ def __init__(self, owner: "ConfiguratorWindow", option: EnvOption) -> None:
self.description = make_label(css_class="choice-doc", ellipsize=True)
text_box.pack_start(self.description, False, False, 0)

value_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
box.pack_end(value_box, False, False, 0)

self.value = make_label(css_class="option-value", selectable=True, ellipsize=True)
self.value.set_xalign(1)
self.value.set_max_width_chars(28)
value_box.pack_start(self.value, False, False, 0)

self.chevron = Gtk.Image.new_from_icon_name("pan-end-symbolic", Gtk.IconSize.MENU)
box.pack_end(self.chevron, False, False, 0)
value_box.pack_start(self.chevron, False, False, 0)

def update(
self,
Expand All @@ -356,7 +368,8 @@ def update(
value_label = tr(language, "default_dynamic")
else:
value_label = state.value if state.value != "" else "<empty>"
self.badge.set_text(f"{source_label}: {value_label}")
self.badge.set_text(source_label)
self.value.set_text(value_label)

if language != self.language:
self.language = language
Expand Down Expand Up @@ -470,10 +483,12 @@ def _build_ui(self) -> None:
self.detail_box.set_border_width(18)
self.detail_header = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.effective_group = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.often_used_group = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.unsupported_group = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.defaults_group = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.detail_box.pack_start(self.detail_header, False, False, 0)
self.detail_box.pack_start(self.effective_group, False, False, 0)
self.detail_box.pack_start(self.often_used_group, False, False, 0)
self.detail_box.pack_start(self.unsupported_group, False, False, 0)
self.detail_box.pack_start(self.defaults_group, False, False, 0)
detail_scroll.add(self.detail_box)
Expand Down Expand Up @@ -662,13 +677,15 @@ def _refresh_details(self) -> None:
for container in (
self.detail_header,
self.effective_group,
self.often_used_group,
self.unsupported_group,
self.defaults_group,
):
for child in container.get_children():
child.destroy()

self.effective_group.hide()
self.often_used_group.hide()
self.unsupported_group.hide()
self.defaults_group.hide()

Expand All @@ -684,20 +701,32 @@ def _refresh_details(self) -> None:
return

self._add_entry_header(entry)
active, defaults = self.store.effective_values(entry)
visible_names = {state.option.name for state in (*active, *defaults)}
active, often_used, defaults = self.store.effective_values(entry)
visible_names = {state.option.name for state in (*active, *often_used, *defaults)}
if self.selected_option_name not in visible_names:
self.selected_option_name = None
self._add_option_group(
self.effective_group, tr(self.language, "effective"), active, entry, default_rows=False
)
if often_used:
self._add_option_group(
self.often_used_group,
tr(self.language, "often_used"),
often_used,
entry,
default_rows=True,
)
self._add_unsupported(entry)
self._add_option_group(
self.defaults_group, tr(self.language, "defaults"), defaults, entry, default_rows=True
)

self.detail_header.show_all()
self.effective_group.show_all()
if often_used:
self.often_used_group.show_all()
else:
self.often_used_group.hide()
self.defaults_group.show_all()
if self.unsupported_group.get_children():
self.unsupported_group.show_all()
Expand Down Expand Up @@ -878,8 +907,8 @@ def toggle_option(self, entry: EntryRecord, option_name: str) -> None:
self._insert_option_editor_after(row.get_parent(), row, entry, state)

def _state_for_option(self, entry: EntryRecord, option_name: str) -> Optional[ValueState]:
active, defaults = self.store.effective_values(entry)
for state in (*active, *defaults):
active, often_used, defaults = self.store.effective_values(entry)
for state in (*active, *often_used, *defaults):
if state.option.name == option_name:
return state
return None
Expand Down
117 changes: 103 additions & 14 deletions configurator/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,54 @@ class ValueState:
SectionIdentity = Tuple[str, str]


OFTEN_USED_OPTION_NAMES = {
"BOX64_DYNAREC_BIGBLOCK",
"BOX64_DYNAREC_CALLRET",
"BOX64_DYNAREC_SAFEFLAGS",
"BOX64_DYNAREC_STRONGMEM",
"BOX64_DYNAREC_DIRTY",
"BOX64_DYNAREC_FASTROUND",
"BOX64_DYNAREC_FASTNAN",
"BOX64_MAXCPU",
}


PROFILE_OPTION_NAME = "BOX64_PROFILE"
PROFILE_DEFAULT_VALUE = "default"
PROFILE_SETTINGS = {
"safest": (
("BOX64_DYNAREC_FASTNAN", "0"),
("BOX64_DYNAREC_FASTROUND", "0"),
("BOX64_DYNAREC_BIGBLOCK", "0"),
("BOX64_DYNAREC_SAFEFLAGS", "2"),
("BOX64_DYNAREC_STRONGMEM", "2"),
),
"safe": (
("BOX64_DYNAREC_BIGBLOCK", "0"),
("BOX64_DYNAREC_SAFEFLAGS", "2"),
("BOX64_DYNAREC_STRONGMEM", "1"),
),
"fast": (
("BOX64_DYNAREC_CALLRET", "1"),
("BOX64_DYNAREC_SEP", "1"),
("BOX64_DYNAREC_BIGBLOCK", "3"),
("BOX64_DYNAREC_SAFEFLAGS", "0"),
("BOX64_DYNAREC_STRONGMEM", "1"),
("BOX64_DYNAREC_DIRTY", "1"),
("BOX64_DYNAREC_FORWARD", "1024"),
),
"fastest": (
("BOX64_DYNAREC_CALLRET", "1"),
("BOX64_DYNAREC_SEP", "2"),
("BOX64_DYNAREC_BIGBLOCK", "3"),
("BOX64_DYNAREC_SAFEFLAGS", "0"),
("BOX64_DYNAREC_STRONGMEM", "0"),
("BOX64_DYNAREC_DIRTY", "1"),
("BOX64_DYNAREC_FORWARD", "1024"),
),
}


class ConfigStore:
def __init__(
self, system_path: Path, user_path: Path, catalog: UsageCatalog, machine_arch: str = ""
Expand Down Expand Up @@ -183,7 +231,10 @@ def fork_entry(self, entry: EntryRecord) -> Tuple[SectionKey, bool]:

def set_option(self, entry: EntryRecord, option_name: str, value: str) -> Tuple[SectionKey, bool]:
section, forked = self._ensure_user_section(entry)
new_key = self.user.set_assignment(section.key, option_name, value)
if self._is_default_profile(option_name, value):
new_key = self.user.remove_assignment(section.key, option_name)
else:
new_key = self.user.set_assignment(section.key, option_name, value)
self._mark_dirty()
return new_key, forked

Expand All @@ -196,11 +247,10 @@ def reset_option(self, entry: EntryRecord, option_name: str) -> SectionKey:
if entry.system_section is not None:
system_value = entry.system_section.last_value(option_name)

new_key = (
self.user.set_assignment(user_section.key, option_name, system_value)
if system_value is not None
else self.user.remove_assignment(user_section.key, option_name)
)
if system_value is not None and not self._is_default_profile(option_name, system_value):
new_key = self.user.set_assignment(user_section.key, option_name, system_value)
else:
new_key = self.user.remove_assignment(user_section.key, option_name)
self._mark_dirty()
return new_key

Expand All @@ -210,31 +260,70 @@ def unsupported_assignments(self, entry: EntryRecord) -> Tuple[Assignment, ...]:
assignment for assignment in direct_box64_assignments(section) if assignment.key not in self.catalog
)

def effective_values(self, entry: EntryRecord) -> Tuple[List[ValueState], List[ValueState]]:
def effective_values(
self, entry: EntryRecord
) -> Tuple[List[ValueState], List[ValueState], List[ValueState]]:
states: Dict[str, ValueState] = {}

if entry.key.kind != "shared":
self._apply_shared(states)
self._apply_matching_wildcards(states, entry)

self._apply_direct(states, entry)
self._apply_profile(states)

active: List[ValueState] = []
often_used: List[ValueState] = []
defaults: List[ValueState] = []
for option in self.catalog.options:
state = states.get(option.name)
if state is None:
defaults.append(
ValueState(
option=option,
value=option.default_value,
source="default",
)
default_state = ValueState(
option=option,
value=option.default_value,
source="default",
)
if option.name in OFTEN_USED_OPTION_NAMES:
often_used.append(default_state)
else:
defaults.append(default_state)
else:
active.append(state)

return active, defaults
active.sort(key=lambda state: state.option.name != PROFILE_OPTION_NAME)
return active, often_used, defaults

def _apply_profile(self, states: Dict[str, ValueState]) -> None:
profile_option = self.catalog.by_name.get(PROFILE_OPTION_NAME)
if profile_option is None:
return

profile_state = states.get(PROFILE_OPTION_NAME)
if profile_state is None:
profile_state = ValueState(
option=profile_option,
value=PROFILE_DEFAULT_VALUE,
source="default",
)
states[PROFILE_OPTION_NAME] = profile_state

for option_name, value in PROFILE_SETTINGS.get(profile_state.value.lower(), ()):
if option_name in states:
continue
option = self.catalog.by_name.get(option_name)
if option is None:
continue
states[option_name] = ValueState(
option=option,
value=value,
source="profile",
)

def _is_default_profile(self, option_name: str, value: Optional[str]) -> bool:
return (
option_name == PROFILE_OPTION_NAME
and (value or "").strip().lower() == PROFILE_DEFAULT_VALUE
)

def _ensure_user_section(self, entry: EntryRecord) -> Tuple[RcSection, bool]:
user_section = self.user.section_for_key(entry.key)
Expand Down
2 changes: 2 additions & 0 deletions configurator/text_en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"save": "Save",
"select_entry": "Select an entry on the left.",
"effective": "Effective options",
"often_used": "Often used options",
"defaults": "Default values",
"unsupported": "Other rcfile keys",
"unsupported_help": "These keys are preserved when a system entry is forked, but this tool does not edit them.",
Expand All @@ -23,6 +24,7 @@
"source_shared-system": "System shared",
"source_wildcard-user": "User wildcard",
"source_wildcard-system": "System wildcard",
"source_profile": "Profile",
"source_default": "Default",
"default_dynamic": "build dependent",
"apply": "Apply",
Expand Down
2 changes: 2 additions & 0 deletions configurator/text_zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"save": "保存",
"select_entry": "请在左侧选择一个条目。",
"effective": "生效选项",
"often_used": "常用选项",
"defaults": "默认值",
"unsupported": "其他配置项",
"unsupported_help": "系统条目复制到用户时会保留这些项,但此工具不会编辑它们。",
Expand All @@ -23,6 +24,7 @@
"source_shared-system": "系统共享",
"source_wildcard-user": "用户通配",
"source_wildcard-system": "系统通配",
"source_profile": "预设",
"source_default": "默认",
"default_dynamic": "随构建而定",
"apply": "应用",
Expand Down
5 changes: 3 additions & 2 deletions configurator/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

_EMBEDDED_USAGE_DATA: Optional[List[Dict[str, Any]]] = globals().get("_EMBEDDED_USAGE_DATA")
_EMBEDDED_USAGE_CN_DATA: Optional[Dict[str, Any]] = globals().get("_EMBEDDED_USAGE_CN_DATA")
ALWAYS_CONFIGURATOR_OPTIONS = {"BOX64_PROFILE"}


def clean_choice_value(raw_key: str) -> str:
Expand Down Expand Up @@ -111,10 +112,10 @@ def load_usage_catalog_from_data(
) -> UsageCatalog:
options: List[EnvOption] = []
for entry in english_data:
if not entry.get("configurator", False):
name = entry["name"]
if not entry.get("configurator", False) and name not in ALWAYS_CONFIGURATOR_OPTIONS:
continue

name = entry["name"]
chinese_entry = chinese_data.get(name, {})
chinese_choices = chinese_entry.get("options", {})
choices = []
Expand Down
2 changes: 1 addition & 1 deletion docs/gen/usage.json
Original file line number Diff line number Diff line change
Expand Up @@ -1821,7 +1821,7 @@
"description": "Predefined sets of environment variables with compatibility or performance in mind.",
"category": "Compatibility",
"wine": true,
"configurator": false,
"configurator": true,
"options": [
{
"key": "safest",
Expand Down
Loading