Shrink window in GTK+ dynamically when content shrinks? - linux

I have a window in a Vala application and an image inside it.
This image is changed sometimes by img.set_from_pixbuf(imgdata); and so it's size changes as well. It's embedded in a Gtk.Box.
box = new Gtk.Box(Orientation.VERTICAL,5);
...
box.pack_end(img,false,false);
So if there was a big image before and I replace it with a smaller one, the window remains ridiculously big and I have not found a method to dynamically shrink it to the space required.
I have tried with window.set_default_size(box.width_request,box.height_request) but it always returns -1.
So any ideas how to resize the window?
Thanks!

I have fought with this issue myself and while the accepted answer is correct, I though I could give a more "complete" answer, with working code.
Reproducing the problem
The following code (In C++, sorry) reproduces your issue:
#include <array>
#include <gtkmm.h>
class ResizableWindow : public Gtk::Window
{
public:
ResizableWindow()
: m_toggle{"Toggle"}
, m_currentImageIndex{0}
{
m_files[0] = "small.png";
m_files[1] = "large.png";
// Setup window layout:
m_layout.attach(*Gtk::manage(new Gtk::Image(m_files[m_currentImageIndex])), 0, 0, 1, 1);
m_layout.attach(m_toggle, 0, 1, 1, 1);
add(m_layout);
// Set up signal handlers:
m_toggle.signal_clicked().connect([this](){OnToggle();});
}
private:
void OnToggle()
{
// Switch image file:
if(m_currentImageIndex == 0)
{
m_currentImageIndex = 1;
}
else
{
m_currentImageIndex = 0;
}
// Load new image.
Gtk::Widget* child = m_layout.get_child_at(0, 0);
Gtk::Image* currentImage = dynamic_cast<Gtk::Image*>(child);
currentImage->set(m_files[m_currentImageIndex]);
}
Gtk::Grid m_layout;
Gtk::Button m_toggle;
std::array<std::string, 2> m_files;
size_t m_currentImageIndex;
};
int main (int argc, char* argv[])
{
auto app = Gtk::Application::create(argc, argv, "so.question.q8903140");
ResizableWindow w;
w.show_all();
return app->run(w);
}
The Toggle button changes the underlying images. Both are the same image, but with different sizes. Notice that, As you already mentionned, when toggling for the first time (small --> large), the window resizes appropriately. However, when toggling a second time (large --> small), the image is resized, but not the window, leaving extra space around the image:
Weird, I know...
Solution
To solve the issue, one needs to call the resize method. So the Toggle handler would become:
void OnToggle()
{
if(m_currentImageIndex == 0)
{
m_currentImageIndex = 1;
}
else
{
m_currentImageIndex = 0;
}
Gtk::Widget* child = m_layout.get_child_at(0, 0);
Gtk::Image* currentImage = dynamic_cast<Gtk::Image*>(child);
currentImage->set(m_files[m_currentImageIndex]);
// Resize window:
resize(1, 1);
}
Note that resize was called with dimensions 1x1 (smallest possible dimensions). Gtkmm will resize the window following geometry constraints automatically from there.

If I am not mistaken the automatic resizing of windows only only happens when elements are too large to be drawn. Additionally the set_default_size method only matters when first drawing the window and unless I am wrong is never used again. I would suggest using the resize method to set the window size. (link)
window.resize(box.width_request, box.height_request);
One thing you need to remember when using resize if you can't resize it smaller than the request_size if you run into that issue use the set_request_size method.

Related

Rendering a ID3D11Texture2D into a SkImage (Skia)

I'm currently trying to create an interop layer to render my render target texture into a Skia SkImage. This is being done to facilitate rendering from my graphics API into Avalonia.
I've managed to piece together enough code to get everything running without any errors (at least, none that I can see), but when I draw the SkImage I see nothing but a black image.
Of course, these things are easier to describe with code:
private EglPlatformOpenGlInterface _platform;
private AngleWin32EglDisplay _angleDisplay;
private readonly int[] _glTexHandle = new int[1];
IDrawingContextImpl context // <-- From Avalonia
_platform = (EglPlatformOpenGlInterface)platform;
_angleDisplay = (AngleWin32EglDisplay)_platform.Display;
IntPtr d3dDevicePtr = _angleDisplay.GetDirect3DDevice();
// Device5 is from SharpDX.
_d3dDevice = new Device5(d3dDevicePtr);
// Texture.GetSharedHandle() is the shared handle of my render target.
_eglTarget = _d3dDevice.OpenSharedResource<Texture2D>(_target.Texture.GetSharedHandle());
// WrapDirect3D11Texture calls eglCreatePbufferFromClientBuffer.
_glSurface = _angleDisplay.WrapDirect3D11Texture(_platform, _eglTarget.NativePointer);
using (_platform.PrimaryEglContext.MakeCurrent())
{
_platform.PrimaryEglContext.GlInterface.GenTextures(1, _glTexHandle);
}
var fbInfo = new GRGlTextureInfo(GlConsts.GL_TEXTURE_2D, (uint)_glTexHandle[0], GlConsts.GL_RGBA8);
_backendTarget = new GRBackendTexture(_target.Width, _target.Height, false, fbInfo);
using (_platform.PrimaryEglContext.MakeCurrent())
{
// Here's where we find the gl surface to our texture object apparently.
_platform.PrimaryEglContext.GlInterface.BindTexture(GlConsts.GL_TEXTURE_2D, _glTexHandle[0]);
EglBindTexImage(_angleDisplay.Handle, _glSurface.DangerousGetHandle(), EglConsts.EGL_BACK_BUFFER);
_platform.PrimaryEglContext.GlInterface.BindTexture(GlConsts.GL_TEXTURE_2D, 0);
}
// context is a GRContext
_skiaSurface = SKImage.FromTexture(context, _backendTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888, SKAlphaType.Premul);
// This clears my render target (obviously). I should be seeing this when I draw the image right?
_target.Clear(GorgonColor.CornFlowerBlue);
canvas.DrawImage(_skiaSurface, new SKPoint(320, 240));
So, as far as I can tell, this should be working. But as I said before, it's only showing me a black image. It's supposed to be cornflower blue. I've tried calling Flush on the ID3D11DeviceContext, but I'm still getting the black image.
Anyone have any idea what I could be doing wrong?

WKWebView: window.innerWidth reporting wrong value

I'm hosting some 3rd-party content in a WKWebView, and their content doesn't resize correctly on orientation change. It does resize correctly in Safari though.
window.onresize = function() { console.log(window.innerWidth) }
This prints the same width every time (768), regardless of orientation, but when I evaluate window.innerWidth on the console after the rotation has completed, it prints the correct width.
Is this a known issue? Are there any workarounds?
UPDATE:
What I ended up doing is re-firing the resize event after a delay. It's ugly but it solves my use case.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil) { _ in
// Orientation change triggers the `resize` event, but `window.innerWidth` is
// wrong *during that time*. By calling resize a second time, we let content resize itself
// according to the correct dimensions.
// There's more! Sometimes the dimensions are still wrong after the rotation has completed,
// so we have to wait. The minimum reliable delay varies widely across devices.
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(750)) {
self.webView?.evaluateJavaScript("window.dispatchEvent(new Event('resize'));", completionHandler: nil)
}
}
}
Yeah I had the same issue. You should use document.body.offsetWidth instead of window.innerWidth, because window.innerWidth can take additional 500ms until it updates itself after the window resize event. This is true at least for WKWebView in iOS.

How to create a button with an icon iside?

as stated in the title question, I want to create a button that uses an icon as background.
I am using the wearable circle emulator with 360x360.
I tried a lot of code and examples but with no success.
Last code used:
static void
create_base_gui(appdata_s *ad)
{
/* Window */
ad->win = elm_win_util_standard_add(PACKAGE, PACKAGE);
elm_win_autodel_set(ad->win, EINA_TRUE);
if (elm_win_wm_rotation_supported_get(ad->win))
{
int rots[4] = { 0, 90, 180, 270 };
elm_win_wm_rotation_available_rotations_set(ad->win, (const int *)(&rots), 4);
}
eext_object_event_callback_add(ad->win, EEXT_CALLBACK_BACK, win_back_cb, ad);
/*Box*/
ad->box = elm_box_add(ad->win);
evas_object_size_hint_weight_set(ad->box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_show(ad->box);
elm_win_resize_object_add(ad->win, ad->box);
ad->button2 = elm_button_add(ad->box);
elm_object_text_set(ad->button2, "Click me");
evas_object_size_hint_weight_set(ad->button2, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
evas_object_size_hint_align_set(ad->button2, EVAS_HINT_FILL, EVAS_HINT_FILL);
ad->icon2 = elm_icon_add(ad->box);
elm_image_file_set(ad->icon2, "C/Tizen/testWorkspace/BasicUI/shared/res/basicui.png", NULL);
elm_image_resizable_set(ad->icon2, EINA_TRUE, EINA_TRUE);
elm_object_part_content_set(ad->button2, "icon", ad->icon2);
elm_object_content_set(ad->button2, ad->icon2);
elm_box_pack_end(ad->box, ad->button2);
evas_object_show(ad->button2);
/* Show window after base gui is set up */
evas_object_show(ad->win);
}
The button is created and it's clickable (no behaviour defined yet).
The icon is not displayed.
What am I doing wrong ?
Thanks
PS: based on https://developer.tizen.org/ko/development/ui-practices/native-application/efl/ui-components/wearable-ui-components/creating-wearable-buttons?langredirect=1
and on the default BasicUI example
You need to declare the path from the tizen system point of view.
The correct path is then
/opt/usr/apps/org.somepackage.yourapp/shared/res/youricon.png
needless to say that org.somepackage.yourapp is the package path to your app and youricon.png is your icon.
You can even check the location of your icon in the device manager.
On the following image it is
/opt/usr/apps/org.example.settingsui/shared/res/settingsui.png
This is not imho very good solution, but there is a better way of doing this:
You can use either function
app_get_shared_resource_path();
app_get_resource_path();
You can write method like this:
static void
app_get_shared_resource(const char *file_in, char *path_out, int path_max)
{
char *res_path = app_get_shared_resource_path();
if (res_path) {
snprintf(path_out, path_max, "%s%s", res_path, file_in);
free(res_path);
}
}
and then use it like this
char icon_path[128] = {0,};
app_get_shared_resource("youricon.png", icon_path, 128);
// create your button and stuff and then
elm_image_file_set(ic, icon_path, NULL);
All these are more valuable for tizen 2.3.X and lower. Since Tizen 2.4 you can use Resource Manager
My guess is that the icon should be added on button control instead of box control.
So this:
ad->icon2 = elm_icon_add(ad->box);
should be:
ad->icon2 = elm_icon_add(ad->button);
Hope that helps!

wxWidgets wxFrame resize in wxNotebook

I am using wxWidget to design GUI. My GUI has multiple tabs. If user resizes the Frame, if he has selected some tab, when he is switching between tabs same modified size should be maintained. i am using sizers to put all child windows.But currently this is not happening. My current code is like this
void XTab::OnPageChanged(wxNotebookEvent & event)
{
if(isActive())
{
int page = event.GetOldSelection();
if(page!=-1)
{
XPanel * panel = (XPanel*)GetPage(page);
xdmlASSERT(panel->isActive());
panel->onUIDeactivate();
}
page = event.GetSelection();
if(page!=-1)
{
XPanel * panel = (XPanel*)GetPage(page);
xdmlASSERT(!panel->isActive());
panel->onUIActivate();
}
RecalcSize();
DFrame::UpdateLayout(this);
}
}
where DFrame is derived from wxFrame and XPanel is derived from wxPanel.The size is recalculated and best size is queried in GetSizer->CalcMin() method. This happens in ReCalcSize(). I don't know the reason why size of frame set by user is not persisting. Is there any way to store modified size of all children dynamically so? can u send some code for this?

Titanium handling different resoutions

Simple question, which is the best way to make sure that the app works on different screen resolutions without looking crap? I can't use static values, then it want adjust according to the resolution. Right now i am using relative measurements (percentage of screen) but wonder if that's really the best way to handle it!?
Another/additional option we have been successful with is a small set of functions which use the screen density value to compute display sizes... this is of course only an approximation, as there are only the four densities, but we have found this to be very helpful.
//=============================================================================
// inch
//
// compute approximate number of pixels for the specified physical on-screen
// size based on the density reported by the OS
//=============================================================================
function inch(size)
{
// default to 160 dpi if unavailable
var height = size * 160.0;
try
{
// compute header height based on screen density ... target .25" height
height = size * Ti.Platform.displayCaps.dpi;
}
catch(e) { warn("Error accessing display caps for screen density calculation: " + e); }
return height;
}
So if you want something to be 3/4 inch high on the screen....
Ti.UI.createThing({ height: inch(.75)});
...or if you want to scale things by point size, one could make some constants...
const g_12pt = inch(12/72); //0.166667
const g_10pt = inch(10/72); //0.138889
const g_8pt = inch(8/72); //0.111111
const g_6pt = inch(6/72); //0.083333
...
...font:{fontSize: g_10pt, fontWeight:"bold"},...
We also created a couple of functions to get the screen height and width, so if we wanted a better layout on a tablet or something tiny we could better understand what we were dealing with.
//=============================================================================
// screenWidth - return screen width in inches
//=============================================================================
function screenWidth()
{
return Titanium.Platform.displayCaps.platformWidth / Titanium.Platform.displayCaps.dpi;
}
//=============================================================================
// screenHeight - return screen height in inches
//=============================================================================
function screenHeight()
{
return Titanium.Platform.displayCaps.platformHeight / Titanium.Platform.displayCaps.dpi;
}
You can go on and on from there... but this really helped us nail down how we laid out our screens on the different densities and platforms. The inch function has exception handling only because we use it early in the app, and sometimes the Ti.Platform is still undefined. By the time we are laying out our reports Ti.Platform is available and so the screen functions do not have the exception handling. If you need to query earlier you may need exception handling in those as well.
Hope this helps!
You'll want to modify your tiapp.xml - in the android section you add a manifest record like so:
<android xmlns:android="http://schemas.android.com/apk/res/android">
<manifest>
<supports-screens android:anyDensity="false"/>
</manifest>
</android>
That will make the app default to the same style that the older versions of titanium used, where it basically scales them according to the device in use. More details on the change can be found here: http://developer.appcelerator.com/blog/2011/06/new-defaults-for-android-layouts-in-1-7.html
well, as far as I googled, the solution is :
1.detect the specific screen resolution:
// app/alloy.js
Alloy.Globals.isIos7Plus = (OS_IOS && parseInt(Ti.Platform.version.split(".")[0]) >= 7);
Alloy.Globals.iPhoneTall = (OS_IOS && Ti.Platform.osname == "iphone" && Ti.Platform.displayCaps.platformHeight == 568);
2.write query style tss:
// Query styles
"#info[if=Alloy.Globals.isIos7Plus]" : {
font : { textStyle : Ti.UI.TEXT_STYLE_FOOTNOTE }
},
"#title[if=Alloy.Globals.isIos7Plus]" : {
top: '25dp', // compensate for the status bar on iOS 7
font : { textStyle : Ti.UI.TEXT_STYLE_HEADLINE }
},
"#content[if=Alloy.Globals.isIos7Plus]" : {
font : { textStyle : Ti.UI.TEXT_STYLE_CAPTION1 }
},
"ScrollView[if=Alloy.Globals.iPhoneTall]" : {
height : '500dp'
}
refer to: http://docs.appcelerator.com/titanium/3.0/#!/guide/Alloy_Styles_and_Themes-section-35621526_AlloyStylesandThemes-Platform-SpecificStyles

Resources