View and search metadata

Is there a "too early" for making a draft PR?
This is a draft, far from being complete. So far, I have added a "metadata" GSetting and menu item that toggles the visibility of an empty TextView inside a Popover to the right of the ImageWindow.

This is what the empty TextView looks like right now.

Here is a Gist with an example that shows how VIPS will be used to access the metadata as though we typed vipsheader -a at the command line.
https://gist.github.com/angstyloop/e0faf930b4978a8fcd24e5c9255f884e

Sure, PR away. You'll probably need a treecolumn to the right rather than a textview.

Sure, PR away. You'll probably need a treecolumn to the right rather than a textview.

Tabular is definitely better! Looks like ColumnView is what GTK4 suggests over TreeViewColumn. I doubt you have a strong opposition to using one over the other, but if so I'm all ears.
I'll probably start with just name and value. The EXIF tags have multiple components sometimes, so those could maybe be handled separately. Tabular name and value is maybe a good starting point? I'm open to whatever.

Now with raw text metadata. Still a work in progress.

Ah columnview, use that, sure. I just got the names mixed up.
I'd think you'd need some kind of search or filter box (metadata can get very large) plus an X button to close the popover again. Beyond that, it's all up to you!
What happens to the popover in fullscreen mode, out of curiosity?

Ah columnview, use that, sure. I just got the names mixed up.
I'd think you'd need some kind of search or filter box (metadata can get very large) plus an X button to close the popover again. Beyond that, it's all up to you!

Since your vast knowledge of GTK over multiple versions is impressive, I'll let this one slide! Haha
There needs to be some way to limit the amount of data shown to the user - I think a search is great.
Also, it would be a way to limit the amount of metadata loaded into memory. As far as I can tell, there is not a limit to the amount of metadata for image formats other than JPEG. I was wondering if loading the full metadata into memory might actually be enough to slow vipsdisp down noticeably when the app starts, or when the popover is opened.

libvips always loads all the metadata, so I don't think there's a useful saving trying to limit it.
(it uses reference counts for metadata items, so they are each only loaded once and it's just pointer copies after that ... no need to worry about efficiency there)

What happens to the popover in fullscreen mode, out of curiosity?

Total weirdness.
If you have the Metadata setting on, so that the popover is open, and then you focus the image window and hit F11, you will go into fullscreen mode. The popover stays visible since its autohide property is false, and it is inconveniently shifted to near the center of the screen, where the image is located, covering it.
At that point, if you hit F11 again, you will exit fullscreen mode, but the popover will disappear completely
If you hit F11 again, you will go into fullscreen mode, still with no popover.
But if you hit F11 again, you will exit fullscreen mode, and the popover is back! The cycle continues if you keep pressing F11.

libvips always loads all the metadata, so I don't think there's a useful saving trying to limit it.
(it uses reference counts for metadata items, so they are each only loaded once and it's just pointer copies after that ... no need to worry about efficiency there)

Wow, that's cool - noted for future reference!
That means I only have to worry about limiting what is shown to the user, which a search seems good for like you said.

With GtkColumnView now

I wonder if instead of a popover, the image window should just get bigger to accomodate a metadata menu next to the image. That would also look good in full screen mode.
Alternatively, it can cover up part of the image, and remain inside the bounds of the image window, but it seems wrong to obscure the image just to see the metadata - not a necessary tradeoff.

If the popover seems fine for now though, I'll jump into search next

Implementing search this week! I will ideally be leveraging GtkSearchEntry widget, which is already debounced out of the box. Pretty cool that GTK provides that. https://docs.gtk.org/gtk4/class.SearchEntry.html
I faster-than-linear search is possible - with a little added memory complexity of course. It might be worthwhile to compare later, but I'll start with the simplest implementation.

Yeah I didn't try to be optimal or anything, but so far I've been able to implement a basic proof-of-concept search UI (outside of VIPSDISP).
I was able to take it one step further and markup the matching substrings. I chose boldface as an example, but I can apply any GTK4-supported CSS to the matching substrings. I can also just forego marking up the substrings if it's undesired.

Sure, bold looks nice!

Works for me then

Since I was already using GRegex for matching and replacing strings, a few small adjustments to the UI allowed for a regex search option. Here is what that looks like.

While a neat demo, this may be overkill for the metadata tag name search.
Although, some of those EXIF tag names can be pretty perilous to type haha

Maybe a fuzzy match would be useful? That's much harder to implement though.

challenge accepted!

I'd like to try something with the Bitap algorithm
Here's what I learned about it:

Apparently, that's what agrep uses

It's considered to be fast for the size of typical pattern strings we are dealing with

It's an on-line algorithm, so it doesn't do indexing, which to me is a plus

There are existing implementations in C I can refer to

The Bitap algorithm has a string size limit on the target string ( the "haystack" ) of either 31 characters or 63 characters. The implementation with 31 characters is at least twice as fast, and probably more because the bit operations are on smaller integer types, but those claims are not backed up by tests I've done myself. I suspect it will be plenty fast.
As for the character limit on the tag name... well, I actually think that is fine...  @jcupitt ?
The EXIF tag names and TIFF tag names exceed 31 characters, let alone 63. Is this on purpose - can I trust it? I'll probably have to track down some really technical IETF document to find out.

Shamelessly stole an example implementation from Wikipedia. The size limit can be doubled by changing unsigned long to unsigned long long and changing 31 to 63.
/*

COMPILE

gcc -o bitapp bitapp.c

RUN

./bitap

*/

 #include <stdlib.h>
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>

/* Find the first match of @pattern in @string with at most @k substitution
 * errors.
 *
 * @text - The string we are searching to find @pattern.
 *
 * @pattern - The string we are trying to find in @text.
 *
 * @k - The maximum number of allowed errors. If k is zero, then the match is
 * exact.
 *
 * @returns A pointer to the character in @text where the first match found
 * starts.
 */
const char *bitap_fuzzy_bitwise_search( const char *text,
	const char *pattern, int k)
{
       const char *result = NULL;
       int m = strlen(pattern);
       unsigned long *R;
       unsigned long pattern_mask[CHAR_MAX+1];
       int i, d;

       if (pattern[0] == '\0') return text;
       if (m > 31) return "The pattern is too long!";

       /* Initialize the bit array R */
       R = malloc((k+1) * sizeof *R);
       for (i=0; i <= k; ++i)
       	R[i] = ~1;

       /* Initialize the pattern bitmasks */
       for (i=0; i <= CHAR_MAX; ++i)
       	pattern_mask[i] = ~0;
       for (i=0; i < m; ++i)
       	pattern_mask[pattern[i]] &= ~(1UL << i);

       for (i=0; text[i] != '\0'; ++i) {
       	/* Update the bit arrays */
       	unsigned long old_Rd1 = R[0];

       	R[0] |= pattern_mask[text[i]];
       	R[0] <<= 1;

       	for (d=1; d <= k; ++d) {
       		unsigned long tmp = R[d];
       		/* Substitution is all we care about */
       		R[d] = (old_Rd1 & (R[d] | pattern_mask[text[i]])) << 1;
       		old_Rd1 = tmp;
       	}

       	if (0 == (R[k] & (1UL << m))) {
       		result = (text+i - m) + 1;
       		break;
       	}
       }

       free(R);
       return result;
}

int main() {
	const char *text = "hhhhhhhhhhhhereeeeeeeeeeeeeeeee";
       const char *pattern = "heee";
       int k = 1;
	const char *a =
       	bitap_fuzzy_bitwise_search( text, pattern, k );
       puts( a );
}

You could do two searches: do a search for exact matches first and display those, then do a second search for bitapp matches on the first 32 characters and show those at the end. You'd probably want exact matches first anyway.
I doubt if speed will be an issue heh. I think I'd do a 64 character match.
I had a quick look and the longest name I found was exif-ifd2-ComponentsConfiguration at 33 characters. I expect there are longer ones.

Oh drat, you have to count the length of the "exif-blah" prefix ...
It's worth mentioning Bitap will find exact matches at the same time as the fuzzy ones, and Bitap with k=0 is exact match. I was just going to order them by increasing number of errors ( usually denoted "k" ). So whenever the tag name has length smaller than 63, I can just use Bitap.
If the length is longer than 63, it would then be necessary to do it exactly like you said. Will it be that long? I'm comfortable having a backup just in case ( unless you think a 63 character tag name simply doesn't exist ).

In theory they can be any length, so I think it would be safest to have a backup.

That's what I'll do then!

It will still work like we talked about, listing the exact matches first, followed by the matches with one wrong character, then two, and so on. I just don't need to treat two separate cases based on pattern length, which should make the code shorter/clearer.

I've been quiet here, so I just wanted to update.
I'm working on a personal project for the Linux 2023 Game Jam hosted by itchi.io, and before that I was playing a lot of video games in my spare time. So I haven't been working on this for a bit. BUT the jam is over in a few days. I'll be back to it pretty shortly after that.
I used VIPS to automate sprite creation. The fill operation was really convenient, as it let me create a "color by coordinates" template. The if-else was what I was using at first - in that case the template and replacement were based on pixel value.
VIPS is used by scientists. But as it turns out, it's also really nice for making sprites for indie games! haha

Yup, TOTK is eating any free time I have heh

TOTK is great - it's fun to just explore and build stuff!
Game jam is finally over! Here's that if you're interested:
https://angstyloop.itch.io/ji/devlog/541996/submitted-ji-to-the-linux-2023-game-jam
I'll be back in VIPSDISP land soon : )

Oh, nice!
I made a few pico8 games, it's a little similar I guess, eg.:
https://www.lexaloffle.com/bbs/?tid=28243
I have a playdate I've been meaning to try stuff on, but no time yet :(

Amazing

Fuzzy match code has matured and is almost ready to drop into VIPSDISP. I will make a small demo GTK app first.
https://gist.github.com/angstyloop/fdd9884b2a4dec8a730e6a697869928c

I wonder if this would be generally useful?
I don't think glib has a fuzzy match function. You could consider making a PR on glib with this code. They'd want to to handle utf-8, of course, which might be annoying to implement.

I get where they're coming from - where would they draw the line, when adding fuzzy match functionality? And would it actually get used? I don't have answers, but getting the good response so quickly from the gnome dev team was cool.
Thank you, and I agree that this seems generally useful. Maybe someone will find my one of my examples and run with it.
For the record, going back to the conversation with the gnome dev, there are definitely libraries for fuzzy matching. However, I think in my specific case, it makes sense to avoid an additional dependency, since all that is desired is some leniency when matching.

Demo of separating exact and fuzzy matches visually for the user

Progress in vipsdisp - exact matches only, no highlighting

Show fuzzy matches if there are no exact matches. Still no highlighting or headings yet

Added text "highlighting" (boldface actually) on exact matches.
Fuzzy matches are still only shown if there are no exact matches, but I removed the hard limit on the number of mismatches. Instead, the largest possible number of mismatches is always the length of the metadata field being examined.
Doing this actually has the nice effect of prioritizing fuzzy matches that are close in terms of hamming distance - a true fuzzy match. This is because strings that are insertions and/or deletions away from the exact match will generally have the fewest mismatches, compared to the other field names.
In the demo, I show some examples of inputs the user might accidentally type. The fuzzy match shows many results for small strings, but that's fine because it actually shows the correct result at the top.

Originally I used GtkPopover to get started, but this this not really the intended use of GtkPopover.
Instead of using a popover, I could

reveal the metadata window in the window with a sliding transition, and
manage the window's size as needed when I reveal or hide the metadata widget, making the window normal size when the metadata widget close button is clicked, and expanding the window when the metadata widget is revealed

Same thing, larger window

Ah nice. Yes, that's probably simpler.

In retrospect, Levenshtein distance would be a better algorithm to use for fuzzy match, since it handles insertions/deletions/substitution errors and has adjustable penalties for these errors.
The Baeza-Yates-Perleberg one is still cool though. I'll make the switch after the metadata editing feature is done.

Since the metadata are just GValues on a VipsImage, the introspection part of this code will look like the save options code.
Should I unpack the "image-description" tag on TIFF images, and let users edit the individual key/value pairs? That would be cool.
At first I will try to just reuse some code from the save options branch to show UI input widgets for the basic types. Then I can try to build UI for the more complicated types as needed, which will also feed back into the save options work.

"image-description" is just a text string, though it can have XML in there.
IPCT is always XML, maybe that would be a good one to support?
https://en.wikipedia.org/wiki/IPTC_Information_Interchange_Model
Though an XML editor is a large job!

Agreed - it would take a while for me to write an XMP editor in C GTK - that probably deserves its own PR. It might be cool to do that later.
I was going to use single-line text fields for strings, but maybe "image-description" specifically can have a multi-line text field, so editing XML would be easier.

All the string fields could be resizeable multiline text fields, but don't know how good it would look. Maybe it wouldn't be so bad.

I am experiencing an issue where my VipsRefStrings are garbage after I try to write to them. Here is a code example:
                                GtkEntryBuffer* buffer = gtk_text_get_buffer( GTK_TEXT( t ) );                                 char *text = g_strdup( gtk_entry_buffer_get_text( buffer ) );                                 VipsRefString *ref_string = vips_ref_string_new( text );                                 vips_image_set_area( image, field_name, free_ref_string, VIPS_AREA( ref_string ) );

Progress: I am able to read and write the metadata fields of a VipsImage (yay).
However, they aren't being saved to the image for some reason. It might be related to the exif-related warnings generated on save. I also seem to be adding an orientation field (and exif tag) that wasn't there before. Maybe they're related.
So there are some issues I need to figure out still, but this is a good checkpoint, since I seem to have gotten over most of my issues reading/writing VipsRefString metadata fields.

Hey, this looks nice!

Do you need the "apply" button?
I think making the whole string editable is probably the best idea. We need better EXIF metadata handling in the libvips core, it's not something to fix in vipsdisp.
You need to copy the image (with vips_copy()) before setting metadata to make sure you have a private VipsImage instance.
I promise I'll spend some time looking at this properly, I'm still catching up from the crunch in June.

Thanks : ) I tried harder than I care to admit lol

Do you need the "apply" button?

No, it's not necessary. It was a good checkpoint, since it saved me from having to write a bit of code. But I don't think it's much more work to make the updates automatically apply to the VipsImage in memory. I'll work on that soon.
I think the user should still have to Save As in order to see the metadata changes in their actual image file. Also, I think the app should tell them if they are closing with unsaved metadata changes. Not hard to implement, just details. Let me know if you're thinking something different though.

I think making the whole string editable is probably the best idea. We need better EXIF metadata handling in the libvips core, it's not something to fix in vipsdisp.

Perfect - I'll leave it as is then.

You need to copy the image (with vips_copy()) before setting metadata to make sure you have a private VipsImage instance.

Okay thank you. I think that information is in the libvips source code and API docs, but I just didn't look hard enough.

I promise I'll spend some time looking at this properly, I'm still catching up from the crunch in June.

Thank you - please don't feel any pressure. I still have stuff to work on, and I haven't even started code cleanup in this branch. You're doing me a favor by letting me poke around and ask questions, and eventually reviewing the code. Also, the code will likely not be perfect on the first round.

Update: I'm confident the vips_copy hint got me closer, but my incorrect usage of the API must run deeper than I thought. No big deal though - I'll just backtrack and look at the source code for vipsheader.c again to get a better idea of how to write metadata using the C API. That code is really clear, does what it's supposed to do, and is easy to test, so it should be an easy exercise for me at this point to pull a minimal example from it. I'll spend time on that this week, and add it to my small but growing collection of libvips examples.
If for whatever reason I can't manage that on my own, I'm just going to tidy it up and leave it for office hours with J : ) I'll technically be a full time student for like 8 weeks starting at the end of this month, so it will finally be quiet here, at least for a while. I'll try to have the code as close to reviewable as I can by then!

How about making a separate widget (Metadata? Header?) to go in the side panel? imagewindow.c is getting a bit unwieldy. I could have a quick go at the boilerplate for this, if you like. It'd be something like infobar.c I guess.

Haha, this works great!
I saw a couple more things:

You have two lines for the header. How about  shrinking this to one line? Get rid of "Apply", have the search box on the right, and keep "Metadata" (or maybe "Header"? Or "Properties"?) in the centre. You could get rid of "Close" as well, or perhaps swap it for an X.
It needs a draggable divider (like eog's). They have their own sidebar widget, which is pretty annoying https://github.com/GNOME/eog/blob/master/src/eog-sidebar.c ... I suppose we'd need to use GtkStack and GtkStackSidebar.
The mousewheel keeps changing the spinbox values :( We'd need to disable this misguided gtk feature.
Various small resize and layout issues I'm sure you're aware of.

I suddenly became really busy a week ago. It feels like time has dilated and a whole month passed, but it's only been a week - does that ever happen to you?
Again, thank you for finishing up SaveOptions and referencing my PR. It's an honor and a privilege to have my name attached to that feature in any capacity!

How about making a separate widget (Metadata? Header?) to go in the side panel? imagewindow.c is getting a bit unwieldy. I could have a quick go at the boilerplate for this, if you like. It'd be something like infobar.c I guess.

Okay! I don't think I need the boilerplate though - I can just look at infobar.c and do my best. I was worried I would waste your time by not doing the GTK things perfectly, but you're unstoppable man. I don't feel bad for not being perfect! Let me give it a shot.

Haha, this works great!
I saw a couple more things:

You have two lines for the header. How about  shrinking this to one line? Get rid of "Apply", have the search box on the right, and keep "Metadata" (or maybe "Header"? Or "Properties"?) in the centre. You could get rid of "Close" as well, or perhaps swap it for an X.
It needs a draggable divider (like eog's). You'd need a GtkPaned for the main image area, with the right-hand pane being the metadata area.
The mousewheel keeps changing the spinbox values :( We'd need to disable this misguided gtk feature.
Various small resize and layout issues I'm sure you're aware of.

Thanks J - feels good. Okay, I think I can handle these tasks. I'll work VISPDISP back into my meager schedule.

glade can be useful for prototyping UI files, though I always end up polishing them by hand.
Did you find ctrl-shift-i? In any gtk4 app, it pops up an inspector you can use to view and change properties, very handy for tweaking values.

The internet really wants me to use glade, but I don't feel like I've done it by hand enough yet to really appreciate glade, if that makes sense. Basically, I like suffering.
I did not know about the GTK4 inspector. That is an incredibly useful tool for debugging and testing! Thanks for the pro tip : )

I have time to work on this tomorrow through Monday - looking forward to it!
I have all the breadcrumbs I need, and I'm feeling motivated.

Today I started working on re-organizing the code related to the metadata viewer/editor into its own widget called Metadata. Since I'm still getting the hang of GObject classes and custom widgets, I referenced existing widget examples in vipsdisp. This involved a lot of copying code, changing the names of things, and moving definitions from file to file. The code isn't expected to compile yet.
I chose to reference the SaveOptions widget because it's easy for me to discern code specific to that feature from code required for all custom widgets, since I helped a bit with that code. InfoBar is still the simplest custom widget I would recommend other people look at in vipsdisp, though.

Today I removed code from metadata.c and metadata.ui that was specific to saveoptions.c and saveoptions.ui, respectively.

I moved some variable definitions and references from metadata.c back into imagewindow.c, and vice-versa, as needed.

I also moved the XML that defines the Metadata widget from imagewindow.ui into metadata.ui

There are still a lot of references to the ImageWindow variable "win" in metadata.c. If those are to be kept, then metadata.c will need to share similarities with infobar.c, so that the Metadata class can have an ImageWindow "win" member variable.

The image_window_metadata function in imagewindow.c still needs to be updated to use the new metadata_new function defined in metadata.c.

The code is still not expected to compile at this point, since there is still a lot to do in this refactor.

Today I made metadata.c more like infobar.c, to give the Metadata class access to ImageWindow methods.

I made Metadata parent class GtkWidget instead of GtkDialog.

I installed a GtkWindow "win" property on Metadata class

I handled TileSource and ImageWindow change in Metadata class.

Besides that, I removed a few lines of dead code, and updated a few variable names.

In metadata.c, I used an ImageWindow "win" pointer on the Metadata widget - passing a Metadata object pointer around as an argument to Metadata methods - instead of passing around an ImageWindow pointer directly.

In imagewindow.c, I passed a Metadata pointer to Metadata methods instead of an ImageWindow pointer.

I initialized the Metadata widget in the metadata_init function instead of the image_window_metadata handler, which is now only responsible for showing or hiding the metadata widget.

I updated the metadata_grid of input widgets dynamically, using the metadata_tile_source_changed handler.

I set the orientation of the main_box in imagewindow.ui, instead of in image_window_metadata.

I added some comments to image_window_metadata, metadata_init, and metadata_tile_source_changed, explaining what the code does.

With these changes complete, the Metadata widget should be a widget now. Like InfoBar, it has access to ImageWindow data and methods, and it knows when a new VipsImage is loaded. This means it can keep itself updated, so ImageWindow is only responsible for showing/hiding the Metadata widget, not creating/destroying it.
I made these changes following InfoBar, which also depends on ImageWindow, and so has good examples of the GObject boilerplate code needed to make this logic work.
That said, this code is probably still not ready to compile. I'll tackle that next time, and make it so that the Metadata widget functions the same as it did before, except now it's actually a Metadata widget.

Also, I haven't forgotten about the other tasks!

Finally, Metadata is a real GtkWidget, and the feature at least has the same functionality as it did before the refactor. Like InfoBar, Metadata updates its ImageWindow pointer, as well as its grid of user input widgets, whenever the TileSource held by ImageWindow changes.
Next up are these items:

Make the header a single line.

Get rid of "Apply"
Put the search box on the right
Keep title in the center
Change title from "Metadata" to "Properties"
Replace "Close" with an "X"

Make a draggable divider (like Eye of Gnome).

Use, e.g., a GtkPaned for the main image area, with the right-hand pane being the metadata area.

Prevent the mousewheel from changing the spinbox values.

Fix various small resize and layout issues.

Oh nice! Well done for pressing on with this.
I made a PR for background update of infobar https://github.com/jcupitt/vipsdisp/pull/22 which makes zoom quite a bit smoother.

Thanks! It's been fun learning more about the GObject, GTK, VIPS. VIPSDISP is a pretty neat application, and I don't even really use it for what it's really good at.
I merged your upstream changes into my fork, and nothing broke.
I'll see what I can knock out tonight :)

I made the made the Metadata widget behave more like the Infobar. Instead of having a Close button, the Metadata view is controlled by a persistent setting in the dropdown menu. There doesn't need to by an Apply button either, since the user still needs to click SaveAs.

Changed "Metadata" to "Properties", in the dropdown menu and metadata widget title.

Used a clever event controller to undo spin button scroll events.

Ouch! That's painful. Many other widgets will also respond to scroll events (option menu, slider, etc), will they need patching too?
nip2 addresses this by hooking into event propagation and blocking all scroll events reaching the window. I don't know if that's possible in gtk4.

Yeah, it's not the best. Blocking events from propagating to the widgets that handle them is still definitely a thing, but that's not as easy to do as it was in GTK3. I'm still learning how to use the EventController API as intended. I went around for a while before settling on what I hope is a temporary fix. It seems like a more "correct" solution would require more knowledge about the widget than its interface provides. This means I need to look at the source code for GtkSpinButton, spend more time poking around at widget internals with the GNOME inspector, or just ask ebassi what to do. I'll do all three!
I "disabled" spin button scroll events by undoing them. I used a capture phase GtkEventControllerScroll to set up a handler that fires every time you scroll on a GtkSpinButton. There is currently one GtkEventControllerScroll and callback function attached to each GtkSpinButton. Other widgets that respond to scroll events are unaffected.
The "correct" approach is what you would think: set up an event controller that captures and blocks propagation of the event in question. This is what ebassi suggested in a forum post for this same issue, which sounds easy enough. However, it doesn't seem to work, nor do any variations of that approach I've tried. Other variations of the "correct" approach would involve me having too much knowledge of the internals of the GtkSpinButton widget, like what it's children are, and what event controllers they use.
I'll seek advice from the GNOME forums myself, since there might be a magic combination of child node, controller type, controller phase, and controller propagation type that blocks the event from reaching the widget that handles it. It may be that the "scroll" event is not the event being propagated, and that I was doing everything else correctly. I'm kind of shooting blind, so I'll just ask what the intended use is. In the meantime, I can dig into the source for GtkSpinButton and use the GNOME inspector to find some of these details.

Oh hey, I figured it out. I must of had something wrong before. I'll experiment to try and figure out my subtle error earlier.
I was looking at gtkspinbutton.c on gitlab and confirmed that GtkSpinButton sets up its EventControllerScroll and listens for "scroll" the same way I was before. So I reverted my code back to that, just to see if it worked, and it did!

/* No-op to prevent @w from propagating "scroll" events it receives.
 */
void disable_scroll_cb( GtkWidget *w ) {}

/* Disable scroll on a widget by adding a capture phase event handler and
 * connecting a no-op callback to the "scroll" event.
 */
static GtkWidget *
disable_scroll( GtkWidget *w )
{
        GtkEventController *ec;

        ec = gtk_event_controller_scroll_new(
                        GTK_EVENT_CONTROLLER_SCROLL_VERTICAL );

        gtk_event_controller_set_propagation_phase( ec, GTK_PHASE_CAPTURE );
        g_signal_connect( ec, "scroll", G_CALLBACK( disable_scroll_cb ), w );
        gtk_widget_add_controller( w, ec );

        return w;
}

/* Create a spin button with range, default value, and optionally enabled
 * scrolling.
 */
GtkWidget *
create_spin_button( double min, double max, double step,
                double value, bool scroll )
{
        GtkWidget *sb;

        sb = gtk_spin_button_new_with_range( min, max, step );
        gtk_spin_button_set_value( GTK_SPIN_BUTTON( sb ), value );

        return scroll ? sb : disable_scroll( sb );
}

Disable scrolling on a GtkScale in GTK4.
/* No-op to prevent @w from propagating "scroll" events it receives.
 */
void disable_scroll_cb( GtkWidget *w ) {}

/* Disable scroll on a widget by adding a capture phase event handler and
 * connecting a no-op callback to the "scroll" event.
 */
static GtkWidget *
disable_scroll( GtkWidget *w )
{
        GtkEventController *ec;

        ec = gtk_event_controller_scroll_new(
                        GTK_EVENT_CONTROLLER_SCROLL_VERTICAL );

        gtk_event_controller_set_propagation_phase( ec, GTK_PHASE_CAPTURE );
        g_signal_connect( ec, "scroll", G_CALLBACK( disable_scroll_cb ), w );
        gtk_widget_add_controller( w, ec );

        return w;
}

/* Create a spin button with range, default value, and optionally enabled
 * scrolling.
 */
GtkWidget *
create_scale( double min, double max, double step,
                double value, bool scroll )
{
        GtkWidget *s;

        s = gtk_scale_new_with_range( GTK_ORIENTATION_HORIZONTAL, min, max, step );
        gtk_range_set_value( GTK_RANGE( s ), value );

        return scroll ? s : disable_scroll( s );
}

The draggable divider behavior isn't set in stone, but it's worth exploring to get a feel the target behavior.

Maybe right-justify the label text? They can get pretty far apart right now, making it hard to see which label matches which widget:

eog also uses paler text for labels, which is nice:

I had a look thorough the code and I think metadata needs a bit of revising.
A widget shouldn't care what it's inside -- they need to be self-contained and only look "inwards", if you see what I mean. If I'm using a widget, I want to be able to drop it in anywhere.
Therefore, the metadata widget should just concern itself with displaying and updating metadata, it shouldn't need to know that it's sitting inside a GtkPaned. All the reveal / hide machinery needs to sit in imagewindow.c, since it's the thing (in imagewindow.ui) that makes the paned widget. For reveal / hide, I think all you need to do is set the pane position, and maybe set visibility if you want to get fancy.
I could have a go at a PR if you like (or maybe you'd rather change it).

I had a look thorough the code and I think metadata needs a bit of revising.
A widget shouldn't care what it's inside -- they need to be self-contained and only look "inwards", if you see what I mean. If I'm using a widget, I want to be able to drop it in anywhere.
Therefore, the metadata widget should just concern itself with displaying and updating metadata, it shouldn't need to know that it's sitting inside a GtkPaned. All the reveal / hide machinery needs to sit in imagewindow.c, since it's the thing (in imagewindow.ui) that makes the paned widget. For reveal / hide, I think all you need to do is set the pane position, and maybe set visibility if you want to get fancy.
I could have a go at a PR if you like (or maybe you'd rather change it).

The Metadata widget doesn't use any GtkPaned methods, fortunately. Instead, it uses GtkRevealer methods supported by the GtkSearchBar created statically with XML in metadata.ui. The GtkPaned is created statically with XML in imagewindow.ui, and is not referenced by its start-child (the image area) or end-child (the metadata widget), which is good and aligned with your comments.
However, the Metadata widget does manipulate the size of the image window in its metadata_show and metadata_hide methods, which is perhaps not aligned with your comments. Besides, widgets that manipulate the size of their containing windows is kind of anti-GTK. I'm sure there is a cleaner approach that takes the sizing and layout responsibility away from the Metadata widget. Let me make some adjustments and then see what you think!

"eog also uses paler text for labels, which is nice"
I agree - that does look nice! There are some other CSS tweaks I'd like to add to the Metadata widget, too, so it will be nice to get the CSS provider machinery in place. I'll add that to my to-do list.

I basically finished the Levenshtein Distance and Longest Common Substring algorithm implementation today. I need to modify it some more to be a drop-in replacement for the Baeza-Yates matching algorithm, but it will support the same highlight-as-you-search functionality that's in place now.
https://gist.github.com/angstyloop/be4d4136b695f8423e9bfdc9c3ca9bcd
/* gcc levdist.c -o levdist && ./levdist */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>

#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))

/* LEVENSHTEIN DISTANCE & LONGEST COMMON SUBSTRING
 *
 * ARGS
 *
 * @s1:	character array 1
 * @s2:	character array 2
 * @n1:	character array 1 size
 * @n2:	string 2 size
 * @r: Result struct, where LD and LCS results are written
 *
 * INFO
 *
 * @s1 and @s2 are character arrays of size @n1 and @n2, which represent two
 * strings. Any null bytes are treated like a normal character, so strings
 * without a terminating null byte are valid input.
 *
 * The LEVENSHTEIN DISTANCE between @s1 and @s2 is the smallest number of
 * insertions, deletions, or substitutions needed to transform @s1 into @s2.
 *
 * The algorithm dynamically builds a table T[n1+1]][n2+1] where the (x, y)
 * entry is the Levenshtein distance between the prefix of @s1 of size y-1 and
 * the prefix of @s2 of size x-1.
 *
 * This implementation uses only a single @v column, updating it in place, since
 * only the elements at (x-1, y-1) and (x, y-1) are needed to compute the
 * element at (x, y). This is expressed by the recursion relation:
 *
 *	k = (s1[y-1] == s2[x-2] ? 0 : 1)
 *
 * 	T[x][y] = MIN3( T[x-1][y] + 1, T[x][y - 1] + 1, T[x-1][y-1] + k )
 *
 * If we let @t1 hold the previous diagonal value, we can rewrite this
 * in terms of the single column @v:
 *
 *	k = (s1[y-1] == s2[x-2] ? 0 : 1)
 *
 * 	v[y] = MIN3( v[y] + 1, v[y-1] + 1, t1 + k )
 *
 * The irst column of the table T is unused, and the first row is
 * used as the base case for the recursion relation.
 *
 * We can get a LONGEST MATCHING SUBSTRING (or all of them) by modifying LDA
 * and taking advantage of the following fact:
 *
 * 	k == 0 for m successive values of y starting at y0
 *			<=>
 * 	exactly match substrings s1[y0 : y0 + m] and s2[x : x + m]
 *
 *
 *                         s2
 *
 *     *----l--e--v--e--n--s--h--t--e--i--n--- x
 *     | .  1  2  3  4  5  6  7  8  9  10 11
 *     |
 *     d .  1  2  3  4  5  6  7  8  9  10 11
 *     |
 *     a .  2  2  3  4  5  6  7  8  9  10 11
 *     |
 *     m .  3  3  3  4  5  6  7  8  9  10 11
 * s1  |
 *     e .  4  3  4  3  4  5  6  7  8  9  10
 *     |
 *     r .  5  4  4  4  4  5  6  7  8  9  10
 *     |
 *     a .  6  5  5  5  5  5  6  7  8  9  10
 *     |
 *     u .  7  6  6  6  6  6  6  7  8  9  10
 *     |
 *
 *     y
 *
 * SYMBOLS
 *
 * @v:	Column vector. Reused and updated in place. The @t1 and @t0 values keep
 *	track of the most recent and second most recent diagonal values.
 * @x:	Row Index.
 * @y:	Column Index.
 * @t0:	Second most recent diagonal value.
 * @t1:	Most recent diagonal value
 * @k:	Test inqeuality in s1[y-1] and s2[x-1], the pair of characters
 * 	corresponding to the table entry T[x][y]. Equals 0 if these characters
 *	are equal, and 1 if they are different.
 *
 * TERMS
 *
 * LD: Levenshtein Distance
 * LCS: Longest Common Substring
 *
 * SOURCES
 *
 * https://en.wikipedia.org/wiki/Levenshtein_distance
 * https://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#C
 */

typedef struct Result Result;
struct Result {
	unsigned ld;
	unsigned nlcs;
	char * lcs1;
	char * lcs2;
};

void
levdist( char *s1, unsigned n1, char *s2, unsigned n2, Result *r ) {
	unsigned *v, x, y, t0, t1, k, nlcs, nlcs0;
	char *lcs1, *lcs2;

	// Initialize variables for computing the first occuring LCS.
	r->nlcs = nlcs = 0;
	r->lcs1 = lcs1 = s1;
	r->lcs2 = lcs2 = s2;

	// Allocate the column.
	v = malloc( (n1 + 1) * sizeof( unsigned ) );

	// Initialize the first column.
	for ( y = 1; y <= n1; y++ )
		v[y] = y;

	// Ignore and don't even bother to initialize the first column. Walk
	// through columns after the first.
	for ( x = 1; x <= n2; x++ ) {
		// The first row (ignoring the first entry) is just the column
		// indices from 1 to n2.
		v[0] = x;

		// The recursion relation defined above and the base case
		// conditions { y = 1, t1 = x - 1 } are used to build the
		// Table T column by column. Only one column @v is needed
		// in memory at a time, and it can be operated on in
		// place, as long as temporary variables @t0 and @t1 are used
		// to keep track of the last diagonal when @v is updated.
		for ( y = 1, t1 = x - 1; y <= n1; y++ ) {
			k = s1[y - 1] == s2[x - 1] ? 0 : 1;

			// LD //
			t0 = v[y];
			v[y] = MIN3( v[y] + 1, v[y - 1] + 1, t1 + k );
			t1 = t0;

			// LCS //
			// If exact match is ongoing
			if ( !k ) {
				// if  exact match started, save location.
				if ( !nlcs ) {
					lcs1 = s1 + y - 1;
					lcs2 = s2 + x - 1;
				}

				// increment lcs size.
				nlcs++;
			// If an exact match ended
			} else if ( nlcs ) {
				// If lcs size > last lcs size...
				if ( nlcs > r->nlcs ) {
					// save match to result
					r->lcs1 = lcs1;
					r->lcs2 = lcs2;
					r->nlcs = nlcs;

					// reset lcs size to zero
					nlcs = 0;
				}
			}

			// If no exact match has started, do nothing.
		}
	}

	// LD //
	// Save Levenshtein Distance to result.
	r->ld = v[n1];

	// Clean up dynamically allocated column @v.
	free( v );
}

int main() {
	char *s1 = "damerau", "levenshtein";
	char *t1, *t2;
	Result r = {0};

	levdist( s1, strlen( s1 ), s2, strlen( s2 ), &r );

	t1 = malloc( r.nlcs + 1 );
	strncpy( t1, r.lcs1, r.nlcs );
	t1[r.nlcs] = 0;

	t2 = malloc( r.nlcs + 1 );
	strncpy( t2, r.lcs2, r.nlcs );
	t2[r.nlcs] = 0;

	printf( "ld=%u\nnlcs=%u\nlcs1=%s\nlcs2=%s\n", r.ld, r.nlcs, t1, t2 );

	free( t1 );
	free( t2 );

	return 0;
}

Ah nice.
Did you see About / Credits btw?

Oh, like the contributors list? Because that was pretty cool!

Reworked match to use Levenshtein distance for inexact matches. Inexact matches are only displayed if there are no exact matches. In that case, a label that reads "No exact matches" is also shown. Levenshtein provides better inexact matches than the previous algorithm.
Cleaned up code. Re-organized some code into separate files. Renamed some symbols.

The metadata widget no longer changes window size when shown or hidden, and no longer uses a GtkRevealer internally to animate show/hide.
Its "revealed" property is just tied to gtk_widget_show / gtk_widget_hide. The metadata widget now fills available space in its containing widget, e.g., a GtkPaned "end-child" container widget. The container is responsible for sizing and layout.

The metadata widget now has support for CSS, and the metadata input labels are colored gray.

Remaining stuff:

sizing and layout (might be done, or close)
investigate why some metadata fields revert when the image is re-opened after save, while others don't

That looks really nice!
I noticed a few small things (I'm sure you have too):

the labels lose their right justification when you start typing in the search box ... even if you delete the search text, they don't get right just back
the "properties" header has a frame around it which could possibly go
you could make it a bit more compact by putting the search box into the "properties" header, perhaps at the right
the properties are properties of the image, not the window, so I think conceptually it'd be better to have the gtkpaned holding the imagedisplay area on the left and props on the right, with the display control bar and the info bar at the top level, something like:

Though editing the .ui file seems to have stopped metadata updating itself, so I guess metadata.c is making assumptions about where it is in the hierarchy.

You could even get rid of the properties header bar and just have a search bar. The placeholder text in the search widget could work as the title:

Sorry, last one, I'd be temped to get rid of the metadata error and warning areas and get metadata.c to display anything like that with the main imagewindow error system.
displaybar has an image-window property and uses that for this kind of thing, for example.

(my changes to the .ui files, in case it's useful https://github.com/jcupitt/vipsdisp/commit/6566307dc991242ed749e7f06f8e051967a89473 )

Thanks J. I’ll work on these later today 🤩

Since none of the actual properties on VipsImage can be persistently changed by vips_image_set, they should just be read-only.
Since the metadata fields of type VIPS_REF_STRING are not persistently modifiable, that part of the UI should also be read-only.
The metadata field "orientation" - an integer type - can be persistently changed. This and other fields deserve an actual input widget.

Since none of the actual properties on VipsImage can be persistently changed by vips_image_set, they should just be read-only.

You're right that things like width and height can't be changed (the image would go crazy!), but everything else can be modified persistently. I think the problem comes with destructive changes (like vips_image_set()) on shared objects.
libvips images are supposed to be immutable, so you can construct them, but once constructed, they are read-only. This restriction makes caching a lot simpler -- images can be shared and reused across threads without the application program knowing about it.
So ... if you vips_image_set() on an image which some other part of the system is also using without your knowledge, your changes can be reversed. The solution is to call vips_copy() on an image before making any changes -- copy has the special property of always making a fresh image reference and it guarantees that no one else has that pointer.
The sequence needs to be:

vips_image_new_from_file(), or vips_text(), or something that makes an image.
image = vips_copy(image); to get a guaranteed unique reference
vips_image_set() to change any metadata item in any way you like, except for the core properties like width and height that directly affect image geometry ... you can set interpretation, for example.
vips_image_write_to_file() and it'll write the image back with the modified metadata.

It looks nice!
I noticed another tiny issue -- panning with mouse drags in the image view area seems to be broken. Perhaps the thing to stop scroll wheel edits has done this?

Thanks J! I'm sure that's all in the documentation, but I think understand the recap.
I'll make a copy and try to use VipsImage methods as much as possible to update properties on the image. Then I'll redo my little tests and see what I can get to persist across app reload.
By the way, one thing I found is that I can set some properties like width and height persistently (meaning persistently over reload) when I use g_object_set. This causes issues when you try to save the image a second time. This should have the same concurrency-related issues as vips_image_set, so I'm not sure what's going on there.

Making the copy before applying changes was probably a good thing to do anyway, but it doesn't seem to have fixed the issues I'm having with saving changes to properties other than "orientation" to the image using the vips_set_* family of methods.
I'm going to get pan working again and just make the UI immutable for now, except maybe orientation, since that's a better checkpoint.  I'm not sure what the deal is and will have to look into it more later.

Wait, in jcupitt/vipsdisp latest I can't pan with mouse drags. Has something else broken it, maybe related to saveoptions?

Hmmm, I just rebuilt everything to double-check check and it's working here. Maybe you're running another version?

Oh weird I must be doing something silly

08a9484698d9464f7ce96a485309229633f11198 is the culprit, which modifies imagewindow.ui

Did some cleaning up of the introspection code in metadata_apply and create_input

I defined some more functions and used a switch statement in create_input function to make it a little easier to look at the code that creates an input widget based on field type. (The code in metadata_apply can be reorganized the same way.)
Made other small changes for readability.

Nice!
Did you see the windows build? It works surprisingly well.
https://github.com/jcupitt/vipsdisp/releases/tag/v2.6.1

Not yet - that's really cool! I also finally found the credits in the application - thanks!

Yes, your code might actually get some large amount of users, heh

In metadata.c and metadatautil.c, I simplified the code that checks the type of each property on a VipsImage.
The "view and search metadata" feature of this PR is looking pretty good... I am tempted to move the "edit metadata" feature to its own PR, where I can focus on getting it working for properties other than just "orientation". What do you think @jcupitt ?

The "view and search metadata" feature of this PR is looking pretty good... I am tempted to move the "edit metadata" feature to its own PR, where I can focus on getting it working for properties other than just "orientation". What do you think @jcupitt ?

Yes, good idea. Let's do a final cleanup, review and merge. Tag this as "ready for review" if you think we're there.

I'll go over it again and do that - thanks J!

Fixed issues with the metadata widget's behavior when opening a new image.
Make sure the grid items are cleaned up by reusing the same statically defined grid and emptying it each time.

I tried with a DICOM and the right-alignment isn't going to work haha

It's unreadable! I think the issue is that libvips property names nest from the left, so they have to be left-aligned.

If I align start, it's not as pretty, but at least you can see the structure.

... and the filter should probably be case-insensitive.

... and the filter should probably be case-insensitive.

Good idea

If I align start, it's not as pretty, but at least you can see the structure.

I can probably also do a better job of distinguishing the rows visually, e.g., by using alternating row background colors.

I used CSS to style the highlighted matching text. This is nice since the label text color is grey, so bold by itself is hard to notice. I also highlight all matches instead of just the first.

Made search case-insensitive, and made labels aligned to start

I need to:

handle flags like saveoptions does
fix nonfatal allocation errors on startup, when metadata setting is toggled on

If I could use g_object_set and g_object_get instead of the vips_image_set family of functions, then the same code could be used in saveoptions.c and metadata.c for dynamically building the UI and updating the object. The vips_image_set/vips_image_get family of functions are recommended by the vipsheader section of the libvips documentation for modifying VipsImage properties, including metadata, but if using g_object_set/g_object_get is an option, it would be nice just from a code perspective. What do you think @jcupitt ? Was this your plan all along ?

They are different things -- saveoptions.c is setting GObject properties (each one is a variable in a class), so it's g_object_set(), but image metadata is something that libvips manages (it's a name-value hash table on the VipsObject base class).

Some of the fields shown in the Metadata widget are actual properties of VipsImage - variables on a class - and can be retrieved with g_object_get. But like you say, this obviously isn't the case for the actual metadata, which must be retrieved with vips_image_get.

I can visually separate the rows by making alternating rows different colors. The issue is there is a gap between my grid columns, even when column-spacing is zero - not sure if this is intentional. Since the row itself can't targeted with CSS, just the elements, this means there is a break in the row color, which looks weird.
Since I only have two columns anyway, I'm going to move away from using GtkGrid in the Metadata widget.

I made the rows easier to tell apart by making the background-color of even and odd rows different. I also moved away from GtkGrid and used some GtkBoxes instead.
I fixed the case insensitive search. It could be toggled with a check mark or something if we wanted, but right now it's just always case-insensitive.

I made the GtkPaned separator (between imagewindow and metadata) visible. The GTK4 GtkPaned docs suggested using GtkFrame widgets to create a gutter that shows the user where the separator is.

I was thinking about this again -- changing metadata is potentially dangerous (you can lose valuable stuff), so we should probably hide it behind a toggle. How about a toggle button at the top right somewhere with that pencil and paper icon? Press that, and the view flips from read-only (what you have now) to a set of editable fields.
Something for the follow-up PR.

Good point - I can definitely do that next PR

Looking nice!
How about just having the frame on the properties side? You still get a visible gutter. and you don't get the extra border on the image. I see:

There's still the extra curved line at the top, but it's hardly visible.
I like the stripes on the proper window. Is it possible to get one of the colours to match the window background exactly? There's a small difference right now.

Ah it's the background of the header bar. We could remove that and just put the search entry into the vbox for the metadata window:

I added some padding too, and removed the other frame to get rid of the curved line. There's still a visible gutter for the user to grab with the  mouse. Maybe it could be a bit bigger?
https://github.com/jcupitt/vipsdisp/commit/24ee5eb11e164f4cc87cab78d57eef3932a20293

Looks good! I'll merge your commit soon and see if I can make the gutter a bit wider.
You reminded me of something - do you think I should make CTRL+F open the search bar whenever the metadata menu is open? While I'm at it, do you want a shortcut (i.e. accelerator) for toggling the properties menu?

I think we're fine without ^F.
loupe and eog seem to use alt-return for properties, so I suppose we should too.
Thinking about this, we also need to support copy-paste and drag-drop, I'll open issues for those.

Sounds good - I'll add the "alt+return" shortcut to this PR.
I think it is a good idea to let the user resize the properties window, because it seems like some of the text in the metadata fields can be quite long, and some users might just want to expand and read, or select and copy portions of the text.
With GTK4, I should be able to animate the hide/reveal of the properties sidebar using CSS transitions. I can add that to this PR.
I was also thinking about copy-and-paste and drag-drop recently...
The property names and values are selectable (and thus copy-pasteable), with a small caveat - the property name is only selectable when it is not highlighted or fully highlighted. I wanted to instead let the user copy a property name or value to the clipboard by simply clicking on it, or clicking on an adjacent button - I was going to ask which you would prefer. I can add that to this PR, or work on it separately with drag-and-drop.
As for drag-and-drop, being able to click and drag a property name or value from the VIPSDISP properties window into an external application would really make this feature shine. GTK4 definitely supports this with its event controller API - it shouldn't be too hard to implement. I can add that to this PR as well, or work on it separately with copy-and-paste.

Merged jcupitt:angstyloop-metadata-viewer into angstyloop:metadata-viewer to get commit 24ee5eb.

Made the paned separator easier to locate and grab by making it wider. Also added some box shadow whenever the user hovers over it.

Oh nice! I like the hover effect.
I think we should probably not add too many more things to this PR. Let's get it merged and make new PRs for any more ideas.

Thanks! I fussed over the CSS for a minute - glad it payed off : )
Yeah that seems reasonable - I'll try to wrap up it up today.

Made Return keyboard shortcut toggle the properties menu.

Animated metadata widget enter/leave. It's pretty slick, but not as concise as I would have liked.
I actually wasn't able to do this with CSS. It's possible to use CSS transitions and animations in GTK4, but the GtkPaned widget complicates things. I also wasn't able to do it with GtkRevealer, again because of interactions with GtkPaned. That doesn't mean GtkPaned is bad! It's just doing a lot for you under the hood and is definitely worth keeping around, in my humble opinion.
I ended up using the GtkPaned API to manipulate the position of the separator inside some g_timeout_add callback functions I wrote: image_window_paned_enter and image_window_paned_leave. I took special care to ensure a few things:

When the user drags the paned separator around, the ImageWindow remembers the new position. We could make that persist across app restart later if we wanted to, e.g. by making it a GSetting. But not in this PR.

When the metadata widget is toggled off and on, the separator will return to the saved position.

When the app is started OR a new image is loaded, if  if the "metadata" GSetting is true, the metadata widget will be revealed, but that doesn't interfere with the scale factor of the ImageDisplay widget.

The user can't cause issues by spamming <Alt>Enter.

I know it's not the most satisfying solution - I generally try to avoid doing animations "by hand" when possible. Hopefully in a future version of GTK, GtkPaned will get an internal GtkRevealer like GtkActionBar has.

I just need to do color matching now on the stripes in the properties window ( maybe - J might have already fixed that in his commit ).
And then the very last thing - and then I promise I'm done - I really do prefer "Properties" over "Metadata". The word "metadata" doesn't appear in GUI anywhere, so I'm just talking about the choice of words in the code. Do you have a strong desire for me to change the names of symbols? It wouldn't be hard, and I wouldn't mind doing it. But if you think it's fine, then I won't fuss over it.

Sure, if you're OK renaming everything, swap it to properties.
I had to animate GtkPaned by hand in nip2 :( It's not a very good solution, as you say. You'd hope gtk would have added this feature by now, ah well.
imagewindow animates a few things by hand too, which is bad. I should look at hooking it up to CSS animations instead.

Great, I’ll work on the symbol changes today! I challenge you to say “metadata editor” five times fast. It’s not physically possible.
That’s validating that you basically did the same thing with GtkPaned (but probably did a better job). It’s such a great widget- I’m sure the GTK devs have a hard enough time jam packing widgets with useful features without cross-cutting their whole design and messing up performance. After all, they did make GtkPaned easy enough to animate by hand by setting the separator position. The toolkit is used on embedded devices, so I understand a lot of thought goes into performance. Speaking of which, I should probably make my hand-rolled animation respect GTK’s “turn off the animations” flag…
Your use case for hand-rolled animation in ImageWindow is probably more legit than mine. If the platform doesn’t support the CSS transitions you need, you still need zoom and pan to work. Having said that, I’m confident it’s possible to accomplish my simple sliding animation with CSS in GTK - it’s just not mission critical for this PR.
I thought about booking into your existing animation strategy, but I didn’t see a clean way to do it, and I thought it would ultimately do more harm than good. It might be a good idea to rename the pan-zoom animation related symbols to have something to do with “scale”, for unfortunate devs like me that learn by reading the code base. Having said that, it’s pretty clear from reading your code why the purpose of those functions is.
Thanks J! This will be my second PR on a FOSS project, like, ever. You’re a solid mentor my dude, without really trying to be. Am I a GNOME developer yet? 😂

I need to spend some time reading about CSS animations in gtk, I don't really understand them :( The animations scrolledwindow does for panning would be a good place to start.
Yes, well done on this PR. Let me know when it's ready for review.

... and have you seen the windows build recently? It's quite a bit better now.

ALRIGHT, respecting  the gtk-enable-animations GtkSetting - while still keeping all the little behaviors the way I said I wanted them - was a huge pain, but I did it. Yay.
It's actually pretty sweet. I hate to do this to you, but can you play around with it at some point when you have time? My brain is tired out for the day. The main things to look for are:

The position of the GtkPaned separator should be "sticky". If you drag it around and pick a position, it should be in that same place when you toggle the Properties widget off and on, or when you open a new image. When animations are enabled, the Properties widget should slide in from the right  edge when you open a new image or toggle the Properties widget on, and it should slide out to the right edge when you toggle it off.

When you open a new image, it should always be full size, even if the GtkPaned separator is all the way to the left edge of the ImageWindow.

If you have the Properties widget open, if should be open when you restart the app ( because it's a GSetting ). The position is not remembered though - there is just a hardcoded initial value. I can save the last position as a GSetting if you want - should just be a few lines, but I'll have to modify the gschema file, and you would have to recompile it, which might be annoying for you ( I don't think the post-install script gets a chance to run, because the build will fail due to missing schema ).

Finally, if you locate the couple lines in image_window_init that set the gtk-enable-animations GtkSetting, and you uncomment them, and change the setting to false, you should see all the same behavior, but without animations.

There are probably too many comments, mostly so I could remember what I was doing, especially in image_window_paned_init and image_window_open, which work together with a handful of boolean flags to handle all these little edge cases. It's kind of gross, but it looks really nice in the UI lol.
Once I'm quadruple-convinced I didn't create any new bugs with these changes, I would love to merge. If not today, then definitely on Monday (PST). Thanks J!

I need to spend some time reading about CSS animations in gtk, I don't really understand them :(

I'm in the same boat

Yes, well done on this PR. Let me know when it's ready for review.

And thanks! One more round of feedback, and then I'll do another sanity check and fix any sneaky issues I missed. Monday (PST) at the latest.

... and have you seen the windows build recently? It's quite a bit better now.

I haven't yet, but I should!

Oh nice! I had a quick play and couldn't break it.
I added a getter for the animation setting and fixed a few small formatting inconsistencies: https://github.com/jcupitt/vipsdisp/commit/67cabc986b1fd24a30786d217c909f6b0ee86e01
I wonder about the 10ms animation time you've used. The display runs at 60hz, so 16 might be a better value. But it should probably be a #define, or pulled from a setting somewhere. I forget what I did for the (not very good) zoom animation.
I tried in valgrind. There's a lot of noise we should remove, but some leaks in this code too, eg:
==1343622== 18 bytes in 2 blocks are definitely lost in loss record 5,206 of 20,054
==1343622==    at 0x4845828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1343622==    by 0x5C5E047: __vasprintf_internal (vasprintf.c:116)
==1343622==    by 0x50E2BA1: g_vasprintf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x50B5810: g_strdup_vprintf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x50B58CC: g_strdup_printf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x128324: create_double_input (propertiesutil.c:382)
...
==1343622== 20 bytes in 7 blocks are definitely lost in loss record 5,391 of 20,054
==1343622==    at 0x4845828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1343622==    by 0x5C5E047: __vasprintf_internal (vasprintf.c:116)
==1343622==    by 0x50E2BA1: g_vasprintf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x50B5810: g_strdup_vprintf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x50B58CC: g_strdup_printf (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x12827F: create_int_input (propertiesutil.c:357)
...
==1343622== 23 bytes in 1 blocks are definitely lost in loss record 5,670 of 20,054
==1343622==    at 0x4845828: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so)
==1343622==    by 0x509ACD1: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x50B4192: g_strdup (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.7800.0)
==1343622==    by 0x5017B20: g_value_set_string (in /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0.7800.0)
==1343622==    by 0x4AD1F63: vips_image_get (in /home/john/vips/lib/libvips.so.42.17.0)
==1343622==    by 0x12841A: properties_util_create_input_box (propertiesutil.c:434)
...

Ah, gtkscrolledwindow has a better animation getter:
static gboolean
gtk_widget_should_animate (GtkWidget *widget)
{
  if (!gtk_widget_get_mapped (widget))
    return FALSE;

  return gtk_settings_get_enable_animations (gtk_widget_get_settings (widget));
}

I remember now -- don't use g_timeout_add(), use gtk_widget_add_tick_callback(). You should probably use an ease function too, eg.:
/* From clutter-easing.c, based on Robert Penner's
 * infamous easing equations, MIT license.
 */
static double
ease_out_cubic (double t)
{
  double p = t - 1;
  return p * p * p + 1;
}

(from the gtk sources)

I added the ease stuff to imagewindow for the discrete zoom animation, maybe it's useful. https://github.com/jcupitt/vipsdisp/commit/947bec9e45a1d6460a9f2e01d877a39165f6eccd
I fixed up the valgrind suppressions file for libvips to remove most of the false alarms for vipsdisp. Master now runs without reported leaks for me.

Thanks for testing! These are good points - I'll probably need an extra day or two, so I'll get back to you by Wednesday with these changes. I'll merge the commit you made into my fork.
Valgrind
I'll run it through valgrind and fix the leaks that aren't part of our baseline. From what I remember on the last PR, the leaks I introduced were pretty easy to spot, since they were mostly related to string handling.
Better animation getter
Evidently, gtk_widget_get_mapped tells you if the widget and all its ancestor widgets are visible. That seems good to have in there.
As for gtk_widget_get_settings, I read in the GTK4 docs that there is only one GtkSettings object per GdkDisplay. I already added a GdkDisplay pointer instance variable to the ImageWindow class in order to make CSS work, so I figured I might as well use gtk_settings_get_for_display. However, I don't actually need to do that - I should just load all the CSS in vipsdispapp.c like you did for SaveOptions. Then the GdkDisplay pointer can be removed from ImageWindow and Properties, and it will make more sense to use gtk_wdget_get_settings like GtkScrolledWindow does.
gtk_widget_add_tick_callback and easing
This is great! Shouldn't be too hard.

Holy memory leaks batman we did it

Just gotta fix the mem leaks I introduced to imagewindow.c, and change how I’m doing the gtkpaned animation. Will probably finish tonight 🤞

I should point out that the memory leaks I fixed earlier - the ones originating in properties.c, propertiesutil.c, and match.c, were only showing up in valgrind when I closed the application or opened a new image. Those probably aren't that big of a deal, but they didn't need to exist, and now they don't!
The only memory leaks originating in imagewindow.c related to my changes occur when the application is closed. The valgrind output indicates they originate in image_window_paned_init, but there isn't anything in there that allocates heap memory (none of my changes to imagewindow.c dynamically allocate memory). These also probably aren't a big deal, since they don't happen during runtime, but if you have anything to say about that @jcupitt, I'll follow whatever advice you've got!
Moving on, now I just need to reimplement my animation the GTK way, and move my CssProviders into vipsdispapp.c.

Fingers crossed, this PR is done! Thanks so much!

I'm still not really certain what governs the automatic clean up of widgets at the bottom of a hierarchy,

When you g_object_unref() a widget, any child widgets are automatically unreffed for you. So usually you just unref the top level thing you want to kill, and everything will be cleaned up and unlinked automatically.
gtk uses "floating references" to make programming a little simpler. When you call eg. x = gtk_label_new(), you get an object with one floating reference in x.
When you first put the label in a container, it "sinks" the floating reference and it becomes a regular reference from the container to the label, and there's no need for you to unref. When the container is unreffed, it'll in turn unref the label, and it'll get cleaned up for you.
There's a chapter in the docs about floating refs:
https://docs.gtk.org/gobject/floating-refs.html

Woah! OK, I'll try it out.

The pane animation looks really nice!
I had a look through propertiesutil.c and I think you're using pspec too much. SaveOptions was all about pspec, since that was introspecting for GObject properties, but with propertiesutil, you need to stick to just GType (with a little pspec just to get the blurb tooltips for the metadata items which are also GObject properties).
I had a quick hack: https://github.com/jcupitt/vipsdisp/commit/5c5f57b58ab5c5fccd4a198e0337a52c9f9e16bf
But that's still using pspec too much. I'll do a bit more.

Here are a couple of commits with more pspec removal, though it's not finished:
https://github.com/jcupitt/vipsdisp/commit/b7f54c34d54a1ed2bde9eff191ad59d7c4d464eb
https://github.com/jcupitt/vipsdisp/commit/58b89c9f3dd9b7d50b414d1276d1ac4ad646fffc

Thanks for explaining floating refs - I'll do some reading on my own too, but what you said makes sense. That explains why gtk_widget_unparent is the way ebassi's blog suggests cleaning up widgets in GTK4. I can get rid of some of the code that explicitly and meticulously cleans up my labels, inputs, boxes, etc.
Here's what I'm using GParamSpec for in propertiesutil.c - let me know what you'd like to see disappear, and what you think is actually absolutely necessary.

getting the blurb for tooltips

getting the nicknames for enums

getting the nicknames for flags

I thought it was cool at first that the create_*_input functions and the properties_util_apply_*_input functions had a common interface. Since I needed a pspec for the flags and enums, I gave the other functions a pspec argument as well. There's really no good reason to do this though. The unused pspec argument can be removed from a lot of functions in propertiesutil.c. I'm guessing your commits get started on that - I'll get those merged soon and get back to you. Probably not going to get this PR in this week after all, but that's okay! I'm not in a rush, and just want to do it right.

Note that when it comes to the actual introspection code - where I'm checking the type of the VipsImage property in order to decide what kind of input to create or what kind of input I'm grabbing a value from -  I'm just using the GType system, like you suggest!
Note also that when a GParamSpec isn't available for a property in cases 1-3 above, I do something different, e.g., I don't add a tooltip, or I show integer values in the UI instead of nicknames.

And thanks by the way - I think the paned animation looks a lot better now! The easing function, and using gtk_widget_add_tick_callback instead of g_timeout_add, made a huge difference.
Note that the reason the GtkPaned animation code might seem a bit more complex than it needs to be, is that it's allowing the VipsImage to render full-size before showing Properties. This was just the easiest way I saw to avoid accidentally shrinking the image.
At the same time, it's respecting the gtk-enable-animations setting, AND it's keeping track of where the user last dragged the paned separator, whenever you show/hide Properties or open a new image. Altogether, there are a lot of little edge cases, which makes the code a little more complex than it would be otherwise. I hope that's alright, because I really like the user experience it creates!

I finished removing pspec from propertiesutil.c: https://github.com/jcupitt/vipsdisp/commit/521b7fc346df5ee978e6332ef236e776aaec9da8
It seems to work OK. I removed the custom css for the tooltip as well, I thought that was probably best left to theme authors.

Definitely not broken, and the code looks great. Thanks!
Good move on removing the tooltip CSS. My tooltip color was just messed up on my Linux Mint distro. Looks fine on Ubuntu 22.

I think it's looking pretty good - do you think it's ready to merge?

I only looked at propertiesutil.c, I'll try properties.c next. I have a deadline on another project, so it might not be until next week.

In properties.c, pspec is only used in the set_property and get_property GObject boilerplate functions.

I read properties.c and thought of a few things:

I think it shouldn't attach to image_window, but to tile_source. It doesn't need any GUI stuff from the main imahge window, it just needs an image to read properties from. This should be a simple thing to change.

I don't think the revealed property on the properties widget is right -- all it does is show/hide the widget. Really, changing revealed ought to trigger the animation.
This is harder to fix. The right solution would be to make our own paned widget to hold all the animation stuff, and put the revealed property on that.
This also has the advantage that we can reuse an animated pane elsewhere in the interface. nip2 (for example) has quite a few animated sliding pullouts.
It would be a harder change to make. Perhaps that's something for a followup PR?

In properties_clear_main_box(), how about just unparenting left_column and right_column, then creating a pair of new vboxes? You don't need to loop and destroy. You could get rid of properties_util_free_label_box() too.

I'll add some small inline comments too.

No it's all good - thank you for the feedback haha. I had some responses, but all your suggestions should be easy to implement, which I'll be able to get to soon!

Hi again, you must be completely sick of this. How about we merge right now, then do any small fixups in follow up PRs? We could get it out of the way for Christmas holidays!

OK, let's merge! Thank you for all your work on this, and happy xmas!

Hey it's merged - nice! I'll still make the changes you asked for. I've just been completely sandbagged at my new job and neglecting mostly everything else.  I should have some time to breathe in a couple weeks.
Thanks for all the help and feedback. Hope you had a good xmas, and happy new year!

Man, this PR is a whopper. Second PR ever! Woot!

It's ok, I've mostly done the changes in master. Maybe pick a new idea for the next thing (if you have time)?


Unable to persist Selection on Watchers for certain type of Request

Describe the bug in current situation
Go to this particular FOI Request, Unable to persists Watchers selection. This is submitted as a personal request and Sync with  AXIS , transitioned to "harms assessment". reproduction steps - TBA
https://test.foirequests.gov.bc.ca/foi/foirequests/539/ministryrequest/764
Link bug to the User Story
Impact of this bug
Describe the impact, i.e. what the impact is, and number of users impacted.
Chance of Occurring (high/medium/low/very low)
Pre Conditions: which Env, any pre-requesites or assumptions to execute steps?
Steps to Reproduce
Steps to reproduce the behavior:

Go to this particular request, https://test.foirequests.gov.bc.ca/foi/foirequests/539/ministryrequest/764
Click on Watcher list
Try to select on user from Watcher list dropdown
Its not persisting the user

Actual/ observed behaviour/ results
Expected behaviour
A clear and concise description of what you expected to happen. Use the gherking language.
Screenshots/ Visual Reference/ Source
If applicable, add screenshots to help explain your problem. You an use screengrab.

This is the Expected Behaviour.
GIVEN an IAO user has clicked the number of watchers
WHEN the dropdown opens
THEN only IAO team members should appear (i.e., no ministry users)
GIVEN a ministry coordinator has clicked the number of watchers
WHEN the dropdown opens
THEN only the ministry team member should appear (i.e., no IAO users)
Based on the above scenarios. This is fixed.
Thanks. @richard-aot

@nkan-aot, appears the watcher is covered by an automated functional test.
Please flag that we should update the automated test to reflect the Expected Behaviour above.

sure, i will update tests to reflect the AC above

Thanks, @nkan-aot. QA Master List had that as the expected behaviour so maybe covered already.

just looked over the AC and automated tests again, it was not covered because I was only checking the correct IAO groups were present (i.e. flex or intake), not necessarily that ministry was NOT present. Also was testing the list at the Open stage so ministry users would not have shown up anyways. The ministry side is a separate test case so that is already covered. will update the tests to cover this case as well


Fix the SAM Slack Community link

Issue #, if available:
#1163
Description of changes:
Found the link while reading the documentation of SAM
Description of how you validated changes:
Clicked and link and signed myself up for getting a Slack invite.
Checklist:

[N/A] Write/update tests
[N/A] make pr passes
[Y] Update documentation
[N/A] Verify transformed template deploys and application functions as expected
[N/A] Add/update example to examples/2016-10-31

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

Codecov Report

Merging #1318 into master will decrease coverage by 0.7%.
The diff coverage is n/a.

@@            Coverage Diff             @@
##           master    #1318      +/-   ##
==========================================
- Coverage    95.3%   94.59%   -0.71%
==========================================
  Files          78       77       -1
  Lines        5171     4129    -1042
  Branches     1190      815     -375
==========================================
- Hits         4928     3906    -1022
+ Misses        113      108       -5
+ Partials      130      115      -15

Impacted Files
Coverage Δ

samtranslator/model/iam.py
81.25% <0%> (-4.47%)
:arrow_down:

samtranslator/swagger/swagger.py
92.57% <0%> (-2.91%)
:arrow_down:

samtranslator/plugins/api/implicit_api_plugin.py
95.57% <0%> (-2.66%)
:arrow_down:

samtranslator/plugins/exceptions.py
83.33% <0%> (-2.39%)
:arrow_down:

samtranslator/model/eventsources/push.py
90.61% <0%> (-1.43%)
:arrow_down:

samtranslator/plugins/globals/globals.py
99.02% <0%> (-0.98%)
:arrow_down:

samtranslator/model/api/api_generator.py
96.72% <0%> (-0.92%)
:arrow_down:

...el/preferences/deployment_preference_collection.py
91.13% <0%> (-0.91%)
:arrow_down:

samtranslator/open_api/open_api.py
93.56% <0%> (-0.83%)
:arrow_down:

samtranslator/model/apigateway.py
97.79% <0%> (-0.66%)
:arrow_down:

... and 21 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 93b6ac0...5347697. Read the comment docs.

Thanks for bringing this up! I just generated a new link and included it in the PR. The splashthat link was a manual process that we've moved off of in recent months.


Add codemod to replace object-keys

This adds a codemod replacement for the object-keys module. This is my first time writing a codemod, so let me know if there's anything I can do to improve this code.
I've also added some improvements to the codebase that I think will be generally useful:

Returned the identifier name from removeImport so that it will work even if the import is renamed to something than what is conventional
Added some types to removeImport

LGTM! Thanks for the PR! One small comment, I think an accidental import made its way into the code. If we remove that, this is good to merge :) Nice work!


Move Hypercall interface in distinct crate

This should enhance readability and allows reusing code in libhermit. Currently, the hypercall interface there is just copy-pasted.
Open points:

[x] Throw out the constants.
[x] Remove unused NETREAD|NETSTAT|NETWRITE hypercalls.

[x] Use a common cargo workspace with uhyve
[x] Ensure stable compilation
[x] Think about a way to make this compatible with non-portIO architectures (aka: not x86 architectures) (related: #324 #325 )
[x] Generalize this from Hypercall Interface to Uhyve-Interface and accommodate things like UART_PORT as well.
[x] Enhance the naming of the parameters.

Thank you for your nice and extensive review! I'll implement the changes, hopefully next week. :-)

Relevant kernel issue: https://github.com/hermitcore/uhyve/pull/452


Update Console IPs for data warehouse ingestion

🚀 Deployed on https://645d4b8dd3b737069e8ba174--cozy-fox-0defec.netlify.app


lower version than IDA6.7

Hi,
I wonder whether I can use lower version than IDA6.7. Since I only have IDA 6.5.  Thanks.

Have you tried?

Hi @pgoodman, I have tired it. When I try /bin/ls, it cannot generate CFG.
./mcsema-disass --disassembler ~/ida-6.5/idal64 --arch amd64 --os linux --output /tmp/ls.cfg --binary /bin/ls --entrypoint main --log_file error.log
Generated an invalid (zero-sized) CFG. Please use the --log_file option to see an error log.

I also try one simple code which has regular version (debug information) and stripped version. For regular version, it can generate CFG. But for stripped version, there will be one error that is "Generated an invalid CFG". Mcsema should support stripped version, right?   I also generate the error report. If you need the error report. Let me know. Thanks.

can you try with the use_remill_semantics branch of mcsema, then attach the log file if there are issues?

What does the log file say when you use the --log_file option? I have a strong suspicion that some of the IDA APIs we use might not be supported in 6.5, but have no concrete way to test that theory.

Unfortunately backward compatibility with IDA Pro is not a priority. In fact, we're planning a transition to IDA 7.0.


Revert "Support DatabaseID in Datastore beam connector (#27987)"

This reverts commit 181296dec2a6d6973e7aaa17f2cb0286c9308457 which did not deal with the default id correctly

Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:

[ ] Mention the appropriate issue in your description (for example: addresses #123), if applicable. This will automatically add a link to the pull request in the issue. If you would like the issue to automatically close on merging the pull request, comment fixes #<ISSUE NUMBER> instead.
[ ] Update CHANGES.md with noteworthy changes.
[ ] If this contribution is large, please file an Apache Individual Contributor License Agreement.

See the Contributor Guide for more tips on how to make review process smoother.
To check the build health, please visit https://github.com/apache/beam/blob/master/.test-infra/BUILD_STATUS.md
GitHub Actions Tests Status (on master branch)

See CI.md for more information about GitHub Actions CI or the workflows README to see a list of phrases to trigger workflows.

R: @johnjcasey

LGTM


Fix regression in gz-cmake that creates build error in gz-rendering

See https://github.com/conda-forge/gz-rendering-feedstock/pull/14#issuecomment-1618514987 .

Checklist

[x] Used a personal fork of the feedstock to propose changes
[x] Bumped the build number (if the version is unchanged)
[ ] Reset the build number to 0 (if the version changed)
[x] Re-rendered with the latest conda-smithy (Use the phrase @conda-forge-admin, please rerender in a comment in this PR for automated rerendering)
[x] Ensured the license file is being packaged.

@conda-forge-admin, please rerender


⚠ Bofrost.it has degraded performance

In f59b232, Bofrost.it (https://www.bofrost.it) experienced degraded performance:

HTTP code: 200
Response time: 1172 ms

Resolved: Bofrost.it performance has improved in d33096b.


Fail to download b2d.iso from local site!!!!

In order to decrease time of downloading, I want to directly reference the b2d.iso file gotten from url before. wdyt?

This is being introduced in https://github.com/docker/machine/pull/424  Thanks for the feedback!  We'll track progress there.  Thanks!


Update HTML Docs or remove them

Jazzy generated docs are old.
Consider updating them or removing them completely

I suggest to remove generated doc. I don't think it brings any value. Zcash SDK is open source and I bet that majority of developers will go to code and check any available documentation there.

I'm finding out if Read The Docs depends on these docs being here, otherwise I will delete them.


Revert "Deprecating G Suite and O365 SSO from E20"

Reverts mattermost/docs#1263

Need to think about this more...


Move accessing API from within pod to tasks

This PR moves https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#accessing-the-api-from-within-a-pod to its own page under the Tasks section.
This is to help address issue #25659.

@sftim Please review when you get the chance.

/assign @sftim

As I added LGTM, I'll let another reviewer mark this for approval.
/lgtm

Page previews:
https://deploy-preview-26601--kubernetes-io-master-staging.netlify.app/docs/tasks/administer-cluster/access-cluster-api/
https://deploy-preview-26601--kubernetes-io-master-staging.netlify.app/docs/tasks/access-api-from-pod/access-api-from-pod/

Previews:

Run applications
Access api from within a Pod
Access clusters using the k8s api

Thanks @uhari03 . Changes look great!
/lgtm

/label tide/merge-method-squash

/approve


Binary find can't find

Binary find throws an exception that doesn't make a lot of sense.
Current behavior
    results.append(mc_reach.compute_network(
  File "fast_reach/mc_reach.pyx", line 143, in mc_reach.compute_network
  File "fast_reach/mc_reach.pyx", line 230, in mc_reach.compute_network
  File "fast_reach/mc_reach.pyx", line 48, in mc_reach.binary_find
ValueError: element 27 not found in [27 26 52 34 42 67 68 71]

Expected behavior
It is pretty clear that 27 is in the list that is being searched, this should not throw an exception, or at least not THIS exception.

Of course it cant, the list wasn't sorted, my bad!


RAMBounceAnimation icon color initialization problem

BounceAnimation/RAMBounceAnimation.swift#L46
is not iconSelectedColor ?

fixed it, now RAMBounceAnimation have iconSelectedColor


change define_state

I propose changing define_state to work like this:
define_state do |v1, v2, v3|
  v1 = "initial value"
  v2 = "some other initial value"
  # v3 never gets initialized
end

You can also do
define_state do |v1 = "foo", v2 = "bar"| end
which is very nice too.
This is possible after ruby 1.9 because you can get the list of parameter names for the block.
This syntax will work only if there are NO define_state parameter... i.e. define_state(:x) will continue to work as it does today.
I will be happy to implement this and do a pull request, I just want some feedback that this will be acceptable.

I'm going to close this for now... This is not going to work as I thought it would.

This issue was moved to reactrb/reactrb#21


Support Raw Statements

Ability to execute raw statements:
current proposal:
var users = db.Select("SELECT * FROM Users"); -- select data
var affected = db.Update("UPDATE Table set A = ?", new [] {1}); -- to update data
var deleted = db.Delete("DELETE FROM Table"); -- to delete from table
db.Statement("exec StoredProcedure ? ?", new [] {1, 2}); -- execute any other kind of queries

Added API:
db.Statement("exec ..."); // to update and execute
db.Select("SELECT * FROM A"); // to select data
db.Select("SELECT * FROM Users");

@ahmad-moussawi
is there a doc? what db.Select("SELECT * FROM A") return?

I mean by users a list of dynamic, and specifically IEnumerable<dynamic>,
off course you can use the generic overload to return IEnumerable<T>.
I will update this in the doc, do you min open an issue under https://github.com/sqlkata/docs


Loved the idea - copied it

Hi! I loved your idea, so I copied it and made it look a little "nicer" and improved the UX a bit to fit my needs!
Have a look: https://github.com/MauriceArikoglu/notion-disable-hover-toggle/tree/main

Hey,
Tried it out. Looks really good. Happy this code was useful for something :D
"Little nicer” is an understatement. I’d say it looks significantly nicer :D
Cheers!
RR

On Feb 13, 2022, at 1:59 PM, Maurice Arikoglu @.***> wrote:
Hi! I loved your idea, so I copied it and made it look a little "nicer" and improved the UX a bit to fit my needs!
Have a look: https://github.com/MauriceArikoglu/notion-disable-hover-toggle/tree/main https://github.com/MauriceArikoglu/notion-disable-hover-toggle/tree/main
—
Reply to this email directly, view it on GitHub https://github.com/Roznoshchik/Notion-no-sidebar/issues/2, or unsubscribe https://github.com/notifications/unsubscribe-auth/AK6JTLP2OOS7HKZ6JOUP36LU26MKRANCNFSM5OI2Y7XQ.
You are receiving this because you are subscribed to this thread.