Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
T
tuneit
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Ximper Linux
tuneit
Commits
e210051a
Commit
e210051a
authored
Jan 18, 2026
by
Kirill Unitsaev
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
widgets: init image widget
parent
420364fc
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
218 additions
and
0 deletions
+218
-0
ImageChooserWidget.py
src/settings/setting/widgets/ImageChooserWidget.py
+213
-0
__init__.py
src/settings/setting/widgets/__init__.py
+5
-0
No files found.
src/settings/setting/widgets/ImageChooserWidget.py
0 → 100644
View file @
e210051a
from
gi.repository
import
Adw
,
Gtk
,
Gdk
from
gi.repository
import
Gio
import
os
from
.BaseWidget
import
BaseWidget
class
ImageChooserWidget
(
BaseWidget
):
def
create_row
(
self
):
self
.
has_file_prefix
=
False
self
.
row
=
Adw
.
PreferencesRow
(
activatable
=
False
,
focusable
=
False
)
main_box
=
Gtk
.
Box
(
orientation
=
Gtk
.
Orientation
.
VERTICAL
,
spacing
=
12
,
margin_top
=
12
,
margin_bottom
=
12
,
margin_start
=
12
,
margin_end
=
12
,
)
if
self
.
setting
.
name
:
title_box
=
Gtk
.
Box
(
orientation
=
Gtk
.
Orientation
.
HORIZONTAL
,
spacing
=
6
,
)
title_label
=
Gtk
.
Label
(
label
=
self
.
setting
.
name
,
halign
=
Gtk
.
Align
.
START
,
hexpand
=
True
,
css_classes
=
[
"title-4"
]
)
title_box
.
append
(
title_label
)
if
self
.
setting
.
help
:
help_label
=
Gtk
.
Label
(
label
=
self
.
setting
.
help
,
halign
=
Gtk
.
Align
.
START
,
css_classes
=
[
"dim-label"
]
)
title_box
.
append
(
help_label
)
main_box
.
append
(
title_box
)
image_frame
=
Gtk
.
Frame
(
css_classes
=
[
"view"
],
halign
=
Gtk
.
Align
.
CENTER
,
)
self
.
picture
=
Gtk
.
Picture
(
content_fit
=
Gtk
.
ContentFit
.
COVER
,
can_shrink
=
True
,
)
self
.
picture
.
set_size_request
(
320
,
180
)
self
.
placeholder
=
Adw
.
StatusPage
(
icon_name
=
"image-missing-symbolic"
,
)
self
.
placeholder
.
set_size_request
(
320
,
180
)
self
.
image_stack
=
Gtk
.
Stack
(
transition_type
=
Gtk
.
StackTransitionType
.
CROSSFADE
,
transition_duration
=
150
,
)
self
.
image_stack
.
add_named
(
self
.
placeholder
,
"placeholder"
)
self
.
image_stack
.
add_named
(
self
.
picture
,
"picture"
)
overlay
=
Gtk
.
Overlay
()
overlay
.
set_child
(
self
.
image_stack
)
self
.
change_button
=
Gtk
.
Button
(
icon_name
=
"document-edit-symbolic"
,
valign
=
Gtk
.
Align
.
END
,
halign
=
Gtk
.
Align
.
END
,
margin_bottom
=
5
,
margin_end
=
5
,
css_classes
=
[
"circular"
,
"secondary"
],
)
self
.
change_button
.
connect
(
"clicked"
,
self
.
_on_change_clicked
)
self
.
image_reset_button
=
Gtk
.
Button
(
icon_name
=
"edit-undo-symbolic"
,
margin_top
=
5
,
margin_end
=
5
,
css_classes
=
[
"circular"
,
"secondary"
],
)
self
.
image_reset_button
.
connect
(
"clicked"
,
self
.
_on_reset_clicked
)
self
.
image_reset_revealer
=
Gtk
.
Revealer
(
transition_type
=
Gtk
.
RevealerTransitionType
.
CROSSFADE
,
transition_duration
=
150
,
child
=
self
.
image_reset_button
,
reveal_child
=
False
,
valign
=
Gtk
.
Align
.
START
,
halign
=
Gtk
.
Align
.
END
)
overlay
.
add_overlay
(
self
.
change_button
)
overlay
.
add_overlay
(
self
.
image_reset_revealer
)
image_frame
.
set_child
(
overlay
)
main_box
.
append
(
image_frame
)
self
.
row
.
set_child
(
main_box
)
self
.
update_display
()
self
.
_update_reset_visibility
()
return
self
.
row
def
_normalize_path
(
self
,
path
):
if
not
path
or
not
isinstance
(
path
,
str
):
return
None
if
path
.
startswith
(
"file://"
):
self
.
has_file_prefix
=
True
path
=
path
[
7
:]
return
os
.
path
.
expandvars
(
os
.
path
.
expanduser
(
path
))
def
update_display
(
self
):
current
=
self
.
_normalize_path
(
self
.
setting
.
_get_backend_value
())
if
current
and
os
.
path
.
exists
(
current
):
file
=
Gio
.
File
.
new_for_path
(
current
)
try
:
texture
=
Gdk
.
Texture
.
new_from_file
(
file
)
self
.
picture
.
set_paintable
(
texture
)
self
.
image_stack
.
set_visible_child_name
(
"picture"
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"Error loading image: {e}"
)
self
.
picture
.
set_paintable
(
None
)
self
.
image_stack
.
set_visible_child_name
(
"placeholder"
)
else
:
self
.
picture
.
set_paintable
(
None
)
self
.
image_stack
.
set_visible_child_name
(
"placeholder"
)
self
.
_update_reset_visibility
()
def
_on_reset_clicked
(
self
,
button
):
default_value
=
self
.
setting
.
default
if
default_value
is
not
None
:
self
.
setting
.
_set_backend_value
(
default_value
)
self
.
update_display
()
def
_update_reset_visibility
(
self
):
current_value
=
self
.
_normalize_path
(
self
.
setting
.
_get_backend_value
())
default_value
=
self
.
_normalize_path
(
self
.
setting
.
default
)
self
.
image_reset_revealer
.
set_reveal_child
(
current_value
!=
default_value
if
default_value
is
not
None
else
False
)
def
_on_change_clicked
(
self
,
button
):
dialog
=
Gtk
.
FileDialog
()
if
'extensions'
in
self
.
setting
.
map
:
filters
=
self
.
_create_file_filters
()
if
filters
.
get_n_items
()
>
0
:
dialog
.
set_filters
(
filters
)
dialog
.
set_default_filter
(
filters
.
get_item
(
0
))
current
=
self
.
_normalize_path
(
self
.
setting
.
_get_backend_value
())
if
current
:
try
:
current_file
=
Gio
.
File
.
new_for_path
(
current
)
parent
=
current_file
.
get_parent
()
if
parent
:
dialog
.
set_initial_folder
(
parent
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"Error setting initial folder: {e}"
)
try
:
dialog
.
open
(
parent
=
button
.
get_root
(),
callback
=
self
.
_on_file_selected
)
except
Exception
as
e
:
self
.
logger
.
error
(
f
"File dialog error: {e}"
)
def
_create_file_filters
(
self
):
filters
=
Gio
.
ListStore
.
new
(
Gtk
.
FileFilter
)
patterns
=
[
p
for
p
in
self
.
setting
.
map
.
get
(
'extensions'
,
[])]
if
patterns
:
file_filter
=
Gtk
.
FileFilter
()
file_filter
.
set_name
(
_
(
"Image files"
))
for
pattern
in
patterns
:
if
pattern
.
startswith
(
'.'
):
file_filter
.
add_suffix
(
pattern
[
1
:])
else
:
file_filter
.
add_pattern
(
pattern
)
filters
.
append
(
file_filter
)
return
filters
def
_on_file_selected
(
self
,
dialog
,
result
):
try
:
file
=
dialog
.
open_finish
(
result
)
if
file
:
path
=
file
.
get_path
()
if
self
.
has_file_prefix
:
path
=
f
"file://{path}"
self
.
setting
.
_set_backend_value
(
path
)
self
.
update_display
()
except
Exception
as
e
:
if
"dismissed"
not
in
str
(
e
)
.
lower
():
self
.
logger
.
error
(
f
"File selection error: {e}"
)
src/settings/setting/widgets/__init__.py
View file @
e210051a
...
...
@@ -2,6 +2,7 @@ from .AvatarWidget import AvatarWidget
from
.BooleanWidget
import
BooleanWidget
from
.ChoiceWidget
import
ChoiceWidget
from
.RadioChoiceWidget
import
RadioChoiceWidget
from
.ThemeChooserWidget
import
ThemeChooserWidget
from
.EntryWidget
import
EntryWidget
from
.NumStepper
import
NumStepper
from
.FileChooser
import
FileChooser
...
...
@@ -9,6 +10,8 @@ from .ButtonWidget import ButtonWidget
from
.InfoLabelWidget
import
InfoLabelWidget
from
.InfoDictWidget
import
InfoDictWidget
from
.DualListWidget
import
DualListWidget
from
.ImageChooserWidget
import
ImageChooserWidget
from
.DualImageChooserWidget
import
DualImageChooserWidget
import
logging
logger
=
logging
.
getLogger
(
f
"{__name__}"
)
...
...
@@ -17,8 +20,10 @@ class WidgetFactory:
widget_map
=
{
'avatar'
:
AvatarWidget
,
'file'
:
FileChooser
,
'image'
:
ImageChooserWidget
,
'choice'
:
ChoiceWidget
,
'choice_radio'
:
RadioChoiceWidget
,
'theme_chooser'
:
ThemeChooserWidget
,
'boolean'
:
BooleanWidget
,
'entry'
:
EntryWidget
,
'number'
:
NumStepper
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment